Mechanism of HasCrossedAbove and HasCrossedBelow

07 Dec 2018, 04:13Mechanism of HasCrossedAbove and HasCrossedBelow#1
Derek Nguyenposts: 8since: 19 Nov 2018

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. 

07 Dec 2018, 04:29#2
Luigi Zollinoposts: 23since: 11 Oct 2016

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

07 Dec 2018, 04:35RE:#3
Derek Nguyenposts: 8since: 19 Nov 2018

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)]? 

07 Dec 2018, 10:30#4
Panagiotis Charalampousposts: 1818since: 13 Jan 2017

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


Head of Community Management at cTrader
07 Dec 2018, 13:20RE:#5
Derek Nguyenposts: 8since: 19 Nov 2018

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

07 Dec 2018, 14:17#6
Panagiotis Charalampousposts: 1818since: 13 Jan 2017

Hi Derek,

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

Best Regards,

Panagiotis


Head of Community Management at cTrader
07 Dec 2018, 14:18RE:#7
Derek Nguyenposts: 8since: 19 Nov 2018

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

07 Dec 2018, 14:33RE:#8
Derek Nguyenposts: 8since: 19 Nov 2018

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

07 Dec 2018, 14:34#9
Panagiotis Charalampousposts: 1818since: 13 Jan 2017

Timeframes?


Head of Community Management at cTrader
07 Dec 2018, 14:35RE:#10
Derek Nguyenposts: 8since: 19 Nov 2018

Panagiotis Charalampous said:

Timeframes?

My bad. It's 5-min