Mechanism of HasCrossedAbove and HasCrossedBelow

Created at 07 Dec 2018, 04: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!
D.

d.vietnguyen1011

Joined 19.11.2018

Mechanism of HasCrossedAbove and HasCrossedBelow
07 Dec 2018, 04:13


Hi Traders/Developers, I am having a hard time warping my head around the HasCrossedAbove/HasCrossedBelow. Suppose I have this code:

if (shortMA.Result.HasCrossedAbove(longMA.Result.LastValue, 1))
{
    ClosePosition(TradeType.Sell);
    if (!PositionExists(TradeType.Buy))
        OpenPosition(TradeType.Buy);
} else if (shortMA.Result.HasCrossedBelow(longMA.Result.LastValue, 1))
{
    ClosePosition(TradeType.Buy);
    if (!PositionExists(TradeType.Sell))
        OpenPosition(TradeType.Sell);
}

As usual, I want the MovingAverageCrossOver to happen at OneLastPreviousBar to enter a position on CurrentBar per

if ((shortSMA.Result.Last(0) > longSMA.Result.Last(0)) && (shortSMA.Result.Last(1) <= longSMA.Result.Last(1))) {}
if ((shortSMA.Result.Last(0) < longSMA.Result.Last(0)) && (shortSMA.Result.Last(1) >= longSMA.Result.Last(1))) {}

When attached to cBot, HasCrossedAbove(HasCrossedBelow) not only adds a new long(short) position when CrossOver occurs but when the existing long(short) position exits due to TakeProfit, a new long(short) position is added immediately after due to shortMA.Result.Last(1) > longMA.Result.Last(1) even though there is no CrossOver.

It seems to me that HasCrossedAbove(HasCrossedBelow) simply just make one comparison between [shortMA.Result.Last(1) && longMA.Result.Last(1)] and execute the ClosePosition() and OpenPosition() based on that condition only. Would be appreciated if anyone can shed light on this confusion. Thanks. 


@d.vietnguyen1011
Replies

mparama
07 Dec 2018, 04:29

Try this:

if (shortMA.Result.HasCrossedAbove(longMA.Result, 0)) 

0 correspond to the LastValue

if (shortMA.Result.HasCrossedAbove(longMA.Result, 1)) 

1 correspond to the previous bar


@mparama

d.vietnguyen1011
07 Dec 2018, 04:35

RE:

mparama said:

Try this:

if (shortMA.Result.HasCrossedAbove(longMA.Result, 0)) 

0 correspond to the LastValue

if (shortMA.Result.HasCrossedAbove(longMA.Result, 1)) 

1 correspond to the previous bar

Thanks mparama.

Does it opens a new position if [shortMA.Result.LastValue > longMA.Result.LastValue] although there is No [shortMA.Result.Last(1) <= longMA.Result.Last(1)]? 


@d.vietnguyen1011

PanagiotisCharalampous
07 Dec 2018, 10:30

Hi Derek,

Can you share the full cBot code as well some steps to reproduce what you see? Maybe the problem is somewhere else.

Best Regards,

Panagiotis


@PanagiotisCharalampous

d.vietnguyen1011
07 Dec 2018, 13:20 ( Updated at: 21 Dec 2023, 09:21 )

RE:

Panagiotis Charalampous said:

Hi Derek,

Can you share the full cBot code as well some steps to reproduce what you see? Maybe the problem is somewhere else.

Best Regards,

Panagiotis

Hi Panagiotis, I manage to fix the problem above however, there are some trades that are not taken by the CrossOver. Below is the code and the problem. Thanks for your time!

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class PracticeBot : Robot
    {

        // +---------------------------
        // | [START] Custom Parameters
        // +---------------------------
        // Robot Name
        [Parameter("Instance Name", DefaultValue = "EMA_cross")]
        public string instanceName { get; set; }

        // Choose the source of calculation
        [Parameter("ShortMASourcePrice")]
        public DataSeries shortSource { get; set; }

        // Choose the source of calculation
        [Parameter("longMASourcePrice")]
        public DataSeries longSource { get; set; }

        // Set Default Lot Size 
        [Parameter("Lot Size", DefaultValue = 0.01)]
        public double lotSize { get; set; }

        // Short and Long Moving Average
        [Parameter("Period SMA #1", DefaultValue = 7, MinValue = 1, MaxValue = 100)]
        public int shortPeriod { get; set; }

        [Parameter("Period SMA #2", DefaultValue = 28, MinValue = 1, MaxValue = 100)]
        public int longPeriod { get; set; }

        [Parameter("Calculate OnBar", DefaultValue = false)]
        public bool calculateOnBar { get; set; }

        // Variables to be initialized in Onstart()
        private ExponentialMovingAverage shortEMA { get; set; }
        private ExponentialMovingAverage longEMA { get; set; }

        [Parameter()]
        public DataSeries sourceSeries { get; set; }
        // +---------------------------
        // | [END] Custom Parameters
        // +---------------------------


        // +-----------------------------
        // | [START] MAIN PROGRAM
        // +-----------------------------
        protected override void OnStart()
        {
            // construct the indicators
            shortEMA = Indicators.ExponentialMovingAverage(shortSource, shortPeriod);
            longEMA = Indicators.ExponentialMovingAverage(longSource, longPeriod);
        }

        /// <summary>
        /// This method is called at every candle (bar) close, when it has formed
        /// </summary>
        protected override void OnBar()
        {
            ManagePositions();
        }

        // [FUNCTION_START] -- Main function to manage Positions
        private void ManagePositions()
        {
            // Generate candle index
            // index 0
            double currentShortMA = shortEMA.Result.Last(0);
            double currentLongMA = longEMA.Result.Last(0);

            // index 1
            double lastShortMA = shortEMA.Result.Last(1);
            double lastLongMA = longEMA.Result.Last(1);

            // index 2
            double twoLastShortMA = shortEMA.Result.Last(2);
            double twoLastLongMA = longEMA.Result.Last(2);

            // Modify Existing Position
            ModifyPosition(TradeType.Buy);
            ModifyPosition(TradeType.Sell);

            // Check Condition for Up
            bool twoBarConditionUp = (currentShortMA > currentLongMA + 0.1 * Symbol.PipSize) && (lastShortMA <= lastLongMA);
            bool threeBarConditionUp = (currentShortMA >= currentLongMA + 0.1 * Symbol.PipSize) && (lastShortMA >= lastLongMA + 0.1 * Symbol.PipSize) && (twoLastShortMA <= twoLastLongMA);

            // Check Condition for Down
            bool twoBarConditionDown = (currentShortMA < currentLongMA - 0.1 * Symbol.PipSize) && (lastShortMA >= lastLongMA);
            bool threeBarConditionDown = (currentShortMA <= currentLongMA - 0.1 * Symbol.PipSize) && (lastShortMA <= lastLongMA - 0.1 * Symbol.PipSize) && (twoLastShortMA >= twoLastLongMA);

            // || threeBarConditionUp) ((twoBarConditionUp && (sourceSeries.Last(0) > currentShortMA) || threeBarConditionUp))
            if (shortEMA.Result.HasCrossedAbove(longEMA.Result, 1))
            {

                if (!PositionExists(TradeType.Buy))
                    //&& (SourceSeries.LastValue > shortEMA.Result.LastValue))
                    OpenPosition(TradeType.Buy);
                ClosePosition(TradeType.Sell);
            }

            // if a sell position is already open and signal is buy do nothing
            //|| threeBarConditionDown) (twoBarConditionDown && (sourceSeries.Last(0) < currentShortMA) || threeBarConditionDown))
            if (shortEMA.Result.HasCrossedBelow(longEMA.Result, 1))
            {
                if (!PositionExists(TradeType.Sell))
                    //&& (SourceSeries.LastValue < shortEMA.Result.LastValue))
                    OpenPosition(TradeType.Sell);
                ClosePosition(TradeType.Buy);
            }
        }

        // [Function_START]--Opens a new long/short position
        private void OpenPosition(TradeType tradeType)
        {
            // calculate volume from lot size.
            double volume = Symbol.QuantityToVolumeInUnits(lotSize);

            // open a new position
            ExecuteMarketOrder(tradeType, Symbol, volume, instanceName, 17, 250);
        }
        // [Function_END]


        // [Function_START]--Close Position
        private void ClosePosition(TradeType tradeType)
        {
            Position p = Positions.Find(instanceName, Symbol, type);

            if (p != null)
            {
                ClosePosition(p);
            }
        }

        // [Function_END]--Close Position

        // [Function_START]-- Retrieve Position by Type to Use Above
        private bool PositionExists(TradeType tradeType)
        {
            var p = Positions.FindAll(instanceName, Symbol, tradeType);

            if (p.Count() >= 1)
            {
                return true;
            }

            return false;
        }
        // [Function_END]-- Retrieve Position by Type to Use Above

        // [Function_START]--ModifyTrailingStop
        private void ModifyPosition(TradeType tradeType)
        {
            Position position = Positions.Find(instanceName, Symbol, tradeType);

            double stopLoss = 0;
            double takeProfit = 0;

            if (position != null)
            {
                if (position.Pips > 30)
                {
                    if (tradeType == TradeType.Buy)
                    {
                        stopLoss = Symbol.Bid - 15 * Symbol.PipSize;
                        takeProfit = Symbol.Bid + 50 * Symbol.PipSize;
                    }
                    else if (tradeType == TradeType.Sell)
                    {
                        stopLoss = Symbol.Ask + 15 * Symbol.PipSize;
                        takeProfit = Symbol.Ask - 50 * Symbol.PipSize;
                    }
                }
                else if (position.Pips > 50)
                {
                    if (tradeType == TradeType.Buy)
                    {
                        stopLoss = Symbol.Bid - 25 * Symbol.PipSize;
                        takeProfit = Symbol.Bid + 100 * Symbol.PipSize;
                    }
                    else if (tradeType == TradeType.Sell)
                    {
                        stopLoss = Symbol.Ask + 25 * Symbol.PipSize;
                        takeProfit = Symbol.Ask - 100 * Symbol.PipSize;
                    }
                }

            }

        }
        // +-----------------------------
        // | [END] MAIN PROGRAM
        // +-----------------------------       
    }
}

* The positions don't exit by the CrossOver


@d.vietnguyen1011

PanagiotisCharalampous
07 Dec 2018, 14:17

Hi Derek,

Can you post a screenshot of the cBot parameters as well so that I can reproduce on backtesting?

Best Regards,

Panagiotis


@PanagiotisCharalampous

d.vietnguyen1011
07 Dec 2018, 14:18

RE:

Panagiotis Charalampous said:

Hi Derek,

Can you post a screenshot of the cBot parameters as well so that I can reproduce on backtesting?

Best Regards,

Panagiotis

I did it with the default value


@d.vietnguyen1011

d.vietnguyen1011
07 Dec 2018, 14:33

RE:

Panagiotis Charalampous said:

Hi Derek,

Can you post a screenshot of the cBot parameters as well so that I can reproduce on backtesting?

Best Regards,

Panagiotis

I take it on GBPJPY fyi


@d.vietnguyen1011

PanagiotisCharalampous
07 Dec 2018, 14:34

Timeframes?


@PanagiotisCharalampous

d.vietnguyen1011
07 Dec 2018, 14:35

RE:

Panagiotis Charalampous said:

Timeframes?

My bad. It's 5-min


@d.vietnguyen1011

PanagiotisCharalampous
07 Dec 2018, 15:11 ( Updated at: 21 Dec 2023, 09:21 )

Strange. I ran it on Spotware Beta 3.4 and works fine. See below

However I noticed that your cBot code has an issue and does not build. So I had to fix it. Can you make sure that you do not get any compilation errors and that you are using the latest version?

Best Regards,

Panagiotis


@PanagiotisCharalampous