How to partially close positions at different levels?

Created at 26 Jan 2022, 14:21
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!
SU

SummerSalt

Joined 30.12.2021

How to partially close positions at different levels?
26 Jan 2022, 14:21


Hello. How can I partially close different quantities of a position at different pip levels? For example, I want to close 1/5 quantity of a position at 20 pips and another 1/5 at 35 pips.

Any and all help will be greatly appreciated. Thanks for your time.


@SummerSalt
Replies

amusleh
27 Jan 2022, 08:55

Hi,

You can use the ClosePosition method overload that accepts an additional volume parameter with position:

ClosePosition(positionToClose, volumeAmountInUnits);

Here is a full example:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class PartialCloseSample : Robot
    {
        private readonly List<long> _firstLevelClosedPositions = new List<long>();
        private readonly List<long> _secondLevelClosedPositions = new List<long>();

        [Parameter("Close %", DefaultValue = 20, Group = "First Level")]
        public double FirstLevelCloseAmountInPercentage { get; set; }

        [Parameter("Pips", DefaultValue = 20, Group = "First Level")]
        public double FirstLevelClosePips { get; set; }

        [Parameter("Close %", DefaultValue = 20, Group = "Second Level")]
        public double SecondLevelCloseAmountInPercentage { get; set; }

        [Parameter("Pips", DefaultValue = 35, Group = "Second Level")]
        public double SecondLevelClosePips { get; set; }

        protected override void OnStart()
        {
            FirstLevelCloseAmountInPercentage /= 100;
            SecondLevelCloseAmountInPercentage /= 100;

            Positions.Opened += Positions_Opened;
            Positions.Closed += Positions_Closed;
        }

        private void Positions_Closed(PositionClosedEventArgs obj)
        {
            // In case position closed fully clean it's enteries from ID collections
            if (Positions.Any(position => position.Id == obj.Position.Id) == false)
            {
                if (_firstLevelClosedPositions.Contains(obj.Position.Id))
                {
                    _firstLevelClosedPositions.Remove(obj.Position.Id);
                }

                if (_secondLevelClosedPositions.Contains(obj.Position.Id))
                {
                    _secondLevelClosedPositions.Remove(obj.Position.Id);
                }
            }

            // If there are other positions from same symbol then don't remove the symbol Tick event handler
            if (Positions.Any(position => position.SymbolName.Equals(obj.Position.SymbolName, StringComparison.Ordinal)))
            {
                return;
            }

            // If there is no other position from the closed position symbol then remove the Tick event handler
            var positionSymbol = Symbols.GetSymbol(obj.Position.SymbolName);

            positionSymbol.Tick -= PositionSymbol_Tick;
        }

        private void Positions_Opened(PositionOpenedEventArgs obj)
        {
            // If there are other positions from same symbol then don't add the symbol Tick event handler
            // Because we already have one
            if (Positions.Count(position => position.SymbolName.Equals(obj.Position.SymbolName, StringComparison.Ordinal)) > 1)
            {
                return;
            }

            // Add position symbol tick event handler
            var positionSymbol = Symbols.GetSymbol(obj.Position.SymbolName);

            positionSymbol.Tick += PositionSymbol_Tick;
        }

        private void PositionSymbol_Tick(SymbolTickEventArgs obj)
        {
            var symbolPositions = Positions.Where(position => position.SymbolName.Equals(obj.SymbolName, StringComparison.Ordinal)).ToArray();

            foreach (var position in symbolPositions)
            {
                if (_firstLevelClosedPositions.Contains(position.Id) == false && position.Pips >= FirstLevelClosePips)
                {
                    ClosePositionByVolumePercenatage(position, FirstLevelCloseAmountInPercentage);

                    _firstLevelClosedPositions.Add(position.Id);
                }
                else if (_secondLevelClosedPositions.Contains(position.Id) == false && position.Pips >= SecondLevelClosePips)
                {
                    ClosePositionByVolumePercenatage(position, SecondLevelCloseAmountInPercentage);

                    _secondLevelClosedPositions.Add(position.Id);
                }
            }
        }

        private void ClosePositionByVolumePercenatage(Position position, double volumePercent)
        {
            var symbol = Symbols.GetSymbol(position.SymbolName);

            var volumeToClose = symbol.NormalizeVolumeInUnits(position.VolumeInUnits * volumePercent);

            ClosePosition(position, volumeToClose);
        }
    }
}

 


@amusleh

SummerSalt
27 Jan 2022, 15:21 ( Updated at: 27 Jan 2022, 15:22 )

amusleh said:

Hi,

You can use the ClosePosition method overload that accepts an additional volume parameter with position:

ClosePosition(positionToClose, volumeAmountInUnits);

Here is a full example:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class PartialCloseSample : Robot
    {
        private readonly List<long> _firstLevelClosedPositions = new List<long>();
        private readonly List<long> _secondLevelClosedPositions = new List<long>();

        [Parameter("Close %", DefaultValue = 20, Group = "First Level")]
        public double FirstLevelCloseAmountInPercentage { get; set; }

        [Parameter("Pips", DefaultValue = 20, Group = "First Level")]
        public double FirstLevelClosePips { get; set; }

        [Parameter("Close %", DefaultValue = 20, Group = "Second Level")]
        public double SecondLevelCloseAmountInPercentage { get; set; }

        [Parameter("Pips", DefaultValue = 35, Group = "Second Level")]
        public double SecondLevelClosePips { get; set; }

        protected override void OnStart()
        {
            FirstLevelCloseAmountInPercentage /= 100;
            SecondLevelCloseAmountInPercentage /= 100;

            Positions.Opened += Positions_Opened;
            Positions.Closed += Positions_Closed;
        }

        private void Positions_Closed(PositionClosedEventArgs obj)
        {
            // In case position closed fully clean it's enteries from ID collections
            if (Positions.Any(position => position.Id == obj.Position.Id) == false)
            {
                if (_firstLevelClosedPositions.Contains(obj.Position.Id))
                {
                    _firstLevelClosedPositions.Remove(obj.Position.Id);
                }

                if (_secondLevelClosedPositions.Contains(obj.Position.Id))
                {
                    _secondLevelClosedPositions.Remove(obj.Position.Id);
                }
            }

            // If there are other positions from same symbol then don't remove the symbol Tick event handler
            if (Positions.Any(position => position.SymbolName.Equals(obj.Position.SymbolName, StringComparison.Ordinal)))
            {
                return;
            }

            // If there is no other position from the closed position symbol then remove the Tick event handler
            var positionSymbol = Symbols.GetSymbol(obj.Position.SymbolName);

            positionSymbol.Tick -= PositionSymbol_Tick;
        }

        private void Positions_Opened(PositionOpenedEventArgs obj)
        {
            // If there are other positions from same symbol then don't add the symbol Tick event handler
            // Because we already have one
            if (Positions.Count(position => position.SymbolName.Equals(obj.Position.SymbolName, StringComparison.Ordinal)) > 1)
            {
                return;
            }

            // Add position symbol tick event handler
            var positionSymbol = Symbols.GetSymbol(obj.Position.SymbolName);

            positionSymbol.Tick += PositionSymbol_Tick;
        }

        private void PositionSymbol_Tick(SymbolTickEventArgs obj)
        {
            var symbolPositions = Positions.Where(position => position.SymbolName.Equals(obj.SymbolName, StringComparison.Ordinal)).ToArray();

            foreach (var position in symbolPositions)
            {
                if (_firstLevelClosedPositions.Contains(position.Id) == false && position.Pips >= FirstLevelClosePips)
                {
                    ClosePositionByVolumePercenatage(position, FirstLevelCloseAmountInPercentage);

                    _firstLevelClosedPositions.Add(position.Id);
                }
                else if (_secondLevelClosedPositions.Contains(position.Id) == false && position.Pips >= SecondLevelClosePips)
                {
                    ClosePositionByVolumePercenatage(position, SecondLevelCloseAmountInPercentage);

                    _secondLevelClosedPositions.Add(position.Id);
                }
            }
        }

        private void ClosePositionByVolumePercenatage(Position position, double volumePercent)
        {
            var symbol = Symbols.GetSymbol(position.SymbolName);

            var volumeToClose = symbol.NormalizeVolumeInUnits(position.VolumeInUnits * volumePercent);

            ClosePosition(position, volumeToClose);
        }
    }
}

 

Thank you very much amusleh! I was not expecting so help. I have another question in regards to creating the close position levels. If for example I wanted to generate a market order OnBar, I use the ExecuteMarketOrder syntax, how then can I generate the LevelClosePips OnBar or OnTimed event. Is there a syntax for this? Thanks again for your time.


@SummerSalt

amusleh
28 Jan 2022, 08:36

RE:

SummerSalt said:

Thank you very much amusleh! I was not expecting so help. I have another question in regards to creating the close position levels. If for example I wanted to generate a market order OnBar, I use the ExecuteMarketOrder syntax, how then can I generate the LevelClosePips OnBar or OnTimed event. Is there a syntax for this? Thanks again for your time.

Hi,

Can you please make your question more clear?


@amusleh

SummerSalt
28 Jan 2022, 12:26 ( Updated at: 31 Jan 2022, 08:40 )

RE: RE:

amusleh said:

SummerSalt said:

Thank you very much amusleh! I was not expecting so help. I have another question in regards to creating the close position levels. If for example I wanted to generate a market order OnBar, I use the ExecuteMarketOrder syntax, how then can I generate the LevelClosePips OnBar or OnTimed event. Is there a syntax for this? Thanks again for your time.

Hi,

Can you please make your question more clear?

Hello

When, I try to use the ClosePosition(positionToClose, volumeAmountInUnits) to close positions, the positions close at the wrong pip levels. I have tried many different combinations involving modfying the entryprice and stoploss which resulted in the right quantity (the percentage of the position that I want to close) of the position being closed but the the pip levels are always wrong. Moreover, the SecondCloseLevel  always closes at the same pip level as the FirstCloseLevel (Which is wrong as well).

E.g: 

 protected override void OnTick()
        {
           CloseLevels();
        }

        private void CloseLevels()
        {
            foreach (var position in Positions)
            {
                if (position.Label == InstanceName)
                {
                    if (position.Pips >= FirstLevelClosePips)
                    {
                        ClosePosition(position, FirstLevelCloseAmountInPercentage);
                    }

                    if (position.Pips >= SecondLevelClosePips)
                    {
                        ClosePosition(position, SecondLevelClosePips);
                    }

                }
            }

        }

 

Thanks for your time.


@SummerSalt

... Deleted by UFO ...

amusleh
31 Jan 2022, 12:38

RE: RE: RE:

SummerSalt said:

Hello

When, I try to use the ClosePosition(positionToClose, volumeAmountInUnits) to close positions, the positions close at the wrong pip levels. I have tried many different combinations involving modfying the entryprice and stoploss which resulted in the right quantity (the percentage of the position that I want to close) of the position being closed but the the pip levels are always wrong. Moreover, the SecondCloseLevel  always closes at the same pip level as the FirstCloseLevel (Which is wrong as well).

E.g: 

 protected override void OnTick()
        {
           CloseLevels();
        }

        private void CloseLevels()
        {
            foreach (var position in Positions)
            {
                if (position.Label == InstanceName)
                {
                    if (position.Pips >= FirstLevelClosePips)
                    {
                        ClosePosition(position, FirstLevelCloseAmountInPercentage);
                    }

                    if (position.Pips >= SecondLevelClosePips)
                    {
                        ClosePosition(position, SecondLevelClosePips);
                    }

                }
            }

        }

 

Thanks for your time.

Hi,

Did you tested the code I posted? or your own code?

The ClosePosition method closes your position whenever you call it, so if the number of Pips aren't correct then the code for checking the position Pips has some issue not the ClosePosition method.


@amusleh

SummerSalt
01 Feb 2022, 10:57 ( Updated at: 01 Feb 2022, 10:59 )

RE: RE: RE: RE:

amusleh said:

SummerSalt said:

Hello

When, I try to use the ClosePosition(positionToClose, volumeAmountInUnits) to close positions, the positions close at the wrong pip levels. I have tried many different combinations involving modfying the entryprice and stoploss which resulted in the right quantity (the percentage of the position that I want to close) of the position being closed but the the pip levels are always wrong. Moreover, the SecondCloseLevel  always closes at the same pip level as the FirstCloseLevel (Which is wrong as well).

E.g: 

 protected override void OnTick()
        {
           CloseLevels();
        }

        private void CloseLevels()
        {
            foreach (var position in Positions)
            {
                if (position.Label == InstanceName)
                {
                    if (position.Pips >= FirstLevelClosePips)
                    {
                        ClosePosition(position, FirstLevelCloseAmountInPercentage);
                    }

                    if (position.Pips >= SecondLevelClosePips)
                    {
                        ClosePosition(position, SecondLevelClosePips);
                    }

                }
            }

        }

 

Thanks for your time.

Hi,

Did you tested the code I posted? or your own code?

The ClosePosition method closes your position whenever you call it, so if the number of Pips aren't correct then the code for checking the position Pips has some issue not the ClosePosition method.

Hello,

I have now tested the code you provided and it works well, thank you very much. I was not able to test it earlier because I did not work set the position.count  to > 2 (my bot executes 2 orders at once), which made me think I had to call your code using the OnTick() function for it to work. However, I am still having the problem of the levels not closing at the exact specified pip amount  (like with StopLoss or Take Profit). Is there a way of improving the accuracy at which the pips close?

Thanks for your patience so far.


@SummerSalt

amusleh
02 Feb 2022, 08:35

RE: RE: RE: RE: RE:

SummerSalt said:

Hello,

I have now tested the code you provided and it works well, thank you very much. I was not able to test it earlier because I did not work set the position.count  to > 2 (my bot executes 2 orders at once), which made me think I had to call your code using the OnTick() function for it to work. However, I am still having the problem of the levels not closing at the exact specified pip amount  (like with StopLoss or Take Profit). Is there a way of improving the accuracy at which the pips close?

Thanks for your patience so far.

Hi,

What do you mean by levels not closing at the exact specified pip amount?

The bot closes a portion of your position when its Pips reach your set amount.


@amusleh

SummerSalt
02 Feb 2022, 09:21 ( Updated at: 02 Feb 2022, 09:47 )

RE: RE: RE: RE: RE: RE:

amusleh said:

SummerSalt said:

Hello,

I have now tested the code you provided and it works well, thank you very much. I was not able to test it earlier because I did not work set the position.count  to > 2 (my bot executes 2 orders at once), which made me think I had to call your code using the OnTick() function for it to work. However, I am still having the problem of the levels not closing at the exact specified pip amount  (like with StopLoss or Take Profit). Is there a way of improving the accuracy at which the pips close?

Thanks for your patience so far.

Hi,

What do you mean by levels not closing at the exact specified pip amount?

The bot closes a portion of your position when its Pips reach your set amount.

Hello,

For Example when I try to close 10% of a position at 100 pips, the bot will sometimes not close till about 120 pips or more, whereas if you set take profit at 100, it will always close at 100 (or as close to 100 depending on the spread). Also, sometimes multiple CloseLevels (the portion of position I am trying to close in %) with different set Pip amounts will close at the same Pip amount or levels are skipped.

 

 


@SummerSalt

SummerSalt
03 Feb 2022, 07:33

RE: RE: RE: RE: RE: RE:

amusleh said:

SummerSalt said:

Hello,

I have now tested the code you provided and it works well, thank you very much. I was not able to test it earlier because I did not work set the position.count  to > 2 (my bot executes 2 orders at once), which made me think I had to call your code using the OnTick() function for it to work. However, I am still having the problem of the levels not closing at the exact specified pip amount  (like with StopLoss or Take Profit). Is there a way of improving the accuracy at which the pips close?

Thanks for your patience so far.

Hi,

What do you mean by levels not closing at the exact specified pip amount?

The bot closes a portion of your position when its Pips reach your set amount.

Hello,

I have been able to resolve the issue by executing multiple market orders async  with the same Stoploss and different TakeProfits. Thank you very much for your time and help.


@SummerSalt