Stochastic cross while under level

Created at 29 Jun 2021, 23:27
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!
WM

wmclennan77

Joined 29.06.2021

Stochastic cross while under level
29 Jun 2021, 23:27


Hi,

I'm trying to create an entry system based on the Stochastic Oscillator.

The rules would be that the K period needs to be crossed up above the D period, while still being under the 80 and 20 levels.

If a cross occurs above or below these levels then a position cannot be created.

What would that code look like?

Specifically, the requirement of below 80/above level bit as I managed to create the Stochastic cross up if statement successfully.


@wmclennan77
Replies

PanagiotisCharalampous
30 Jun 2021, 08:50

Hi wmclennan77,

It would look like this

           if(stochastic.PercentK.Last(1) > 80)
           {
            // do something
           }

Best Regards,

Panagiotis 

Join us on Telegram and Facebook


@PanagiotisCharalampous

wmclennan77
30 Jun 2021, 22:14

RE:

PanagiotisCharalampous said:

Hi wmclennan77,

It would look like this

           if(stochastic.PercentK.Last(1) > 80)
           {
            // do something
           }

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

Hi Panagiotis,

If you don't mind, would you take a look at my code?

When running a backtest, the robot creates no positions at all.

It is my first robot, so I'm unsure.

 

using System;
using System.Linq;
using cAlgo.API;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace FirstBot
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]

    public class cAlgo : Robot
    {
        private double _volumeInUnits;

        [Parameter("Label", DefaultValue = "Sample")]
        public string Label { get; set; }

        [Parameter("Volume (Lots)", DefaultValue = 0.01)]
        public double VolumeInLots { get; set; }

        [Parameter(DefaultValue = 100, MinValue = 1)]
        public int StopLoss { get; set; }

        [Parameter(DefaultValue = 150, MinValue = 1)]
        public int TakeProfit { get; set; }

        [Parameter("MACD LongCycle", DefaultValue = 26, MinValue = 1)]
        public int LongCycle { get; set; }

        [Parameter("MACD ShortCycle", DefaultValue = 12, MinValue = 1)]
        public int ShortCycle { get; set; }

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

        [Parameter("MA Type", DefaultValue = 6)]
        public MovingAverageType MaType { get; set; }

        [Parameter("K Periods", DefaultValue = 9)]
        public int KPeriods { get; set; }

        [Parameter("D Periods", DefaultValue = 9)]
        public int DPeriods { get; set; }

        [Parameter("K Slowing", DefaultValue = 3)]
        public int K_Slowing { get; set; }

        public DataSeries Price { get; set; }

        [Parameter("MA Bias", DefaultValue = 50)]
        public int MA_Bias { get; set; }

        public static TimeFrame Minute15;

        private MacdCrossOver _MACD;
        private StochasticOscillator _SOC;
        private ExponentialMovingAverage _MA_50;
        bool MACDBuy;
        bool MACDSell;
        bool SOCBuy;
        bool SOCSell;
        bool MA_BiasBuy;
        bool MA_BiasSell;

        protected override void OnStart()
        {
            _volumeInUnits = Symbol.QuantityToVolumeInUnits(VolumeInLots);
            MACDBuy = false;
            MACDSell = false;
            SOCBuy = false;
            SOCSell = false;
            MA_BiasBuy = false;
            MA_BiasSell = false;
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, MACDPeriod);
            _SOC = Indicators.StochasticOscillator(KPeriods, K_Slowing, DPeriods, MaType);
            _MA_50 = Indicators.ExponentialMovingAverage(Price, MA_Bias);
        }

        protected override void OnBar()
        {
            var ActiveBuy = Positions.Find(Label, SymbolName, TradeType.Buy);
            var ActiveSell = Positions.Find(Label, SymbolName, TradeType.Sell);

            // MACD cross
            if (_MACD.MACD.Last(1) < _MACD.Signal.Last(1) && _MACD.MACD.Last(0) > _MACD.Signal.Last(0) && _MACD.Signal.Last(0) < 0)
            {
                MACDBuy = true;
            }

            else if (_MACD.MACD.Last(1) > _MACD.Signal.Last(1) && _MACD.MACD.Last(0) < _MACD.Signal.Last(0) && _MACD.Signal.Last(0) > 0)
            {
                MACDSell = true;

            }

            // Stochastic cross
            if (_SOC.PercentK.HasCrossedAbove(_SOC.PercentD, 0) && _SOC.PercentK.Last(1) <= 20)
            {
                if (_SOC.PercentK.Last(1) > 80)
                {
                    SOCBuy = true;
                }

            }

            else if (_SOC.PercentK.HasCrossedBelow(_SOC.PercentD, 0) && _SOC.PercentK.Last(1) >= 80)
            {
                if (_SOC.PercentK.Last(1) < 20)
                {
                    SOCSell = true;
                }
            }

            // Moving Average Bias
            if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasBuy = true;
            }

            else if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasSell = true;
            }

            if (MACDBuy == true && SOCBuy == true && MA_BiasBuy == true && ActiveBuy == null)
            {
                ClosePosition(ActiveSell);
                ExecuteMarketOrder(TradeType.Buy, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }

            if (MACDSell == true && SOCSell == true && MA_BiasSell == true && ActiveSell == null)
            {
                ClosePosition(ActiveBuy);
                ExecuteMarketOrder(TradeType.Sell, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }


        }

    }

}


@wmclennan77

amusleh
01 Jul 2021, 11:31

Hi,

Your cBot code had few bugs that were causing it to crash, I fixed it but there is still no trade because your entry logic is very strict:

using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace FirstBot
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class cAlgo : Robot
    {
        private double _volumeInUnits;

        [Parameter("Label", DefaultValue = "Sample")]
        public string Label { get; set; }

        [Parameter("Volume (Lots)", DefaultValue = 0.01)]
        public double VolumeInLots { get; set; }

        [Parameter(DefaultValue = 100, MinValue = 1)]
        public int StopLoss { get; set; }

        [Parameter(DefaultValue = 150, MinValue = 1)]
        public int TakeProfit { get; set; }

        [Parameter("MACD LongCycle", DefaultValue = 26, MinValue = 1)]
        public int LongCycle { get; set; }

        [Parameter("MACD ShortCycle", DefaultValue = 12, MinValue = 1)]
        public int ShortCycle { get; set; }

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

        [Parameter("MA Type", DefaultValue = 6)]
        public MovingAverageType MaType { get; set; }

        [Parameter("K Periods", DefaultValue = 9)]
        public int KPeriods { get; set; }

        [Parameter("D Periods", DefaultValue = 9)]
        public int DPeriods { get; set; }

        [Parameter("K Slowing", DefaultValue = 3)]
        public int K_Slowing { get; set; }

        [Parameter("Price")]
        public DataSeries Price { get; set; }

        [Parameter("MA Bias", DefaultValue = 50)]
        public int MA_Bias { get; set; }

        public static TimeFrame Minute15;

        private MacdCrossOver _MACD;
        private StochasticOscillator _SOC;
        private ExponentialMovingAverage _MA_50;
        private bool MACDBuy;
        private bool MACDSell;
        private bool SOCBuy;
        private bool SOCSell;
        private bool MA_BiasBuy;
        private bool MA_BiasSell;

        protected override void OnStart()
        {
            _volumeInUnits = Symbol.QuantityToVolumeInUnits(VolumeInLots);
            MACDBuy = false;
            MACDSell = false;
            SOCBuy = false;
            SOCSell = false;
            MA_BiasBuy = false;
            MA_BiasSell = false;
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, MACDPeriod);
            _SOC = Indicators.StochasticOscillator(KPeriods, K_Slowing, DPeriods, MaType);
            _MA_50 = Indicators.ExponentialMovingAverage(Price, MA_Bias);
        }

        protected override void OnBar()
        {
            var ActiveBuy = Positions.Find(Label, SymbolName, TradeType.Buy);
            var ActiveSell = Positions.Find(Label, SymbolName, TradeType.Sell);

            // MACD cross
            if (_MACD.MACD.Last(1) < _MACD.Signal.Last(1) && _MACD.MACD.Last(0) > _MACD.Signal.Last(0) && _MACD.Signal.Last(0) < 0)
            {
                MACDBuy = true;
            }
            else if (_MACD.MACD.Last(1) > _MACD.Signal.Last(1) && _MACD.MACD.Last(0) < _MACD.Signal.Last(0) && _MACD.Signal.Last(0) > 0)
            {
                MACDSell = true;
            }

            // Stochastic cross
            if (_SOC.PercentK.HasCrossedAbove(_SOC.PercentD, 0) && _SOC.PercentK.Last(1) <= 20)
            {
                if (_SOC.PercentK.Last(1) > 80)
                {
                    SOCBuy = true;
                }
            }
            else if (_SOC.PercentK.HasCrossedBelow(_SOC.PercentD, 0) && _SOC.PercentK.Last(1) >= 80)
            {
                if (_SOC.PercentK.Last(1) < 20)
                {
                    SOCSell = true;
                }
            }
            // Moving Average Bias

            if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasBuy = true;
            }
            else if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasSell = true;
            }

            if (MACDBuy == true && SOCBuy == true && MA_BiasBuy == true && ActiveBuy == null)
            {
                if (ActiveSell != null)
                {
                    ClosePosition(ActiveSell);
                }

                ExecuteMarketOrder(TradeType.Buy, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }

            if (MACDSell == true && SOCSell == true && MA_BiasSell == true && ActiveSell == null)
            {
                if (ActiveBuy != null)
                {
                    ClosePosition(ActiveBuy);
                }

                ExecuteMarketOrder(TradeType.Sell, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }
        }
    }
}

If you remove some of the entry conditions you will see trades on back test, also please don't user the Last(0) or LastValue on your cBot, it gives you the latest not closed value of a data series, which can be non deterministic, instead use Last(1) and Last(2).

If you couldn't figure out how to develop the cBot based on your requirements you can post a job request or ask one of our consultants to do it for you.


@amusleh

wmclennan77
02 Jul 2021, 12:38

RE:

amusleh said:

Hi,

Your cBot code had few bugs that were causing it to crash, I fixed it but there is still no trade because your entry logic is very strict:

using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace FirstBot
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class cAlgo : Robot
    {
        private double _volumeInUnits;

        [Parameter("Label", DefaultValue = "Sample")]
        public string Label { get; set; }

        [Parameter("Volume (Lots)", DefaultValue = 0.01)]
        public double VolumeInLots { get; set; }

        [Parameter(DefaultValue = 100, MinValue = 1)]
        public int StopLoss { get; set; }

        [Parameter(DefaultValue = 150, MinValue = 1)]
        public int TakeProfit { get; set; }

        [Parameter("MACD LongCycle", DefaultValue = 26, MinValue = 1)]
        public int LongCycle { get; set; }

        [Parameter("MACD ShortCycle", DefaultValue = 12, MinValue = 1)]
        public int ShortCycle { get; set; }

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

        [Parameter("MA Type", DefaultValue = 6)]
        public MovingAverageType MaType { get; set; }

        [Parameter("K Periods", DefaultValue = 9)]
        public int KPeriods { get; set; }

        [Parameter("D Periods", DefaultValue = 9)]
        public int DPeriods { get; set; }

        [Parameter("K Slowing", DefaultValue = 3)]
        public int K_Slowing { get; set; }

        [Parameter("Price")]
        public DataSeries Price { get; set; }

        [Parameter("MA Bias", DefaultValue = 50)]
        public int MA_Bias { get; set; }

        public static TimeFrame Minute15;

        private MacdCrossOver _MACD;
        private StochasticOscillator _SOC;
        private ExponentialMovingAverage _MA_50;
        private bool MACDBuy;
        private bool MACDSell;
        private bool SOCBuy;
        private bool SOCSell;
        private bool MA_BiasBuy;
        private bool MA_BiasSell;

        protected override void OnStart()
        {
            _volumeInUnits = Symbol.QuantityToVolumeInUnits(VolumeInLots);
            MACDBuy = false;
            MACDSell = false;
            SOCBuy = false;
            SOCSell = false;
            MA_BiasBuy = false;
            MA_BiasSell = false;
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, MACDPeriod);
            _SOC = Indicators.StochasticOscillator(KPeriods, K_Slowing, DPeriods, MaType);
            _MA_50 = Indicators.ExponentialMovingAverage(Price, MA_Bias);
        }

        protected override void OnBar()
        {
            var ActiveBuy = Positions.Find(Label, SymbolName, TradeType.Buy);
            var ActiveSell = Positions.Find(Label, SymbolName, TradeType.Sell);

            // MACD cross
            if (_MACD.MACD.Last(1) < _MACD.Signal.Last(1) && _MACD.MACD.Last(0) > _MACD.Signal.Last(0) && _MACD.Signal.Last(0) < 0)
            {
                MACDBuy = true;
            }
            else if (_MACD.MACD.Last(1) > _MACD.Signal.Last(1) && _MACD.MACD.Last(0) < _MACD.Signal.Last(0) && _MACD.Signal.Last(0) > 0)
            {
                MACDSell = true;
            }

            // Stochastic cross
            if (_SOC.PercentK.HasCrossedAbove(_SOC.PercentD, 0) && _SOC.PercentK.Last(1) <= 20)
            {
                if (_SOC.PercentK.Last(1) > 80)
                {
                    SOCBuy = true;
                }
            }
            else if (_SOC.PercentK.HasCrossedBelow(_SOC.PercentD, 0) && _SOC.PercentK.Last(1) >= 80)
            {
                if (_SOC.PercentK.Last(1) < 20)
                {
                    SOCSell = true;
                }
            }
            // Moving Average Bias

            if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasBuy = true;
            }
            else if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasSell = true;
            }

            if (MACDBuy == true && SOCBuy == true && MA_BiasBuy == true && ActiveBuy == null)
            {
                if (ActiveSell != null)
                {
                    ClosePosition(ActiveSell);
                }

                ExecuteMarketOrder(TradeType.Buy, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }

            if (MACDSell == true && SOCSell == true && MA_BiasSell == true && ActiveSell == null)
            {
                if (ActiveBuy != null)
                {
                    ClosePosition(ActiveBuy);
                }

                ExecuteMarketOrder(TradeType.Sell, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }
        }
    }
}

If you remove some of the entry conditions you will see trades on back test, also please don't user the Last(0) or LastValue on your cBot, it gives you the latest not closed value of a data series, which can be non deterministic, instead use Last(1) and Last(2).

If you couldn't figure out how to develop the cBot based on your requirements you can post a job request or ask one of our consultants to do it for you.

Hi amusleh,

Thanks a lot for your assistance regarding code bugs. I'm new to C# as I have transitioned from Obj Pascal, but I enjoy trading and learning.

I have realised that there are definitely some logic errors as this strategy is one I've traded manually and definitely works. It's not the most profitable, but easy enough to learn on.

 

The core idea is that on every candle close, provided that there is no trade open already, if the MACD is crossed up, the Stochastic is crossed up and below the stated level, and the position of price in relation to the EMA is the same, then a position will open.

On backtest it seems like the stochastic is what's causing the problems, as I've removed and individually tested each component. Also, the bot seems to have trouble opening short positions at all, which leads to a terrible equity graph.

 

Is the strategy accurately reflected in the code? Any input would be great appreciated.


@wmclennan77

amusleh
02 Jul 2021, 18:50

Hi,

There was an issue on your code, why you were checking once if the Stochastic PercentK is below or equal to 20 and then inside next code block you have another if statement that checks if its greater than 80? how should it work? try this:

using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace FirstBot
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class cAlgo : Robot
    {
        private double _volumeInUnits;

        [Parameter("Label", DefaultValue = "Sample")]
        public string Label { get; set; }

        [Parameter("Volume (Lots)", DefaultValue = 0.01)]
        public double VolumeInLots { get; set; }

        [Parameter(DefaultValue = 100, MinValue = 1)]
        public int StopLoss { get; set; }

        [Parameter(DefaultValue = 150, MinValue = 1)]
        public int TakeProfit { get; set; }

        [Parameter("MACD LongCycle", DefaultValue = 26, MinValue = 1)]
        public int LongCycle { get; set; }

        [Parameter("MACD ShortCycle", DefaultValue = 12, MinValue = 1)]
        public int ShortCycle { get; set; }

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

        [Parameter("MA Type", DefaultValue = 6)]
        public MovingAverageType MaType { get; set; }

        [Parameter("K Periods", DefaultValue = 9)]
        public int KPeriods { get; set; }

        [Parameter("D Periods", DefaultValue = 9)]
        public int DPeriods { get; set; }

        [Parameter("K Slowing", DefaultValue = 3)]
        public int K_Slowing { get; set; }

        [Parameter("Price")]
        public DataSeries Price { get; set; }

        [Parameter("MA Bias", DefaultValue = 50)]
        public int MA_Bias { get; set; }

        public static TimeFrame Minute15;

        private MacdCrossOver _MACD;
        private StochasticOscillator _SOC;
        private ExponentialMovingAverage _MA_50;
        private bool MACDBuy;
        private bool MACDSell;
        private bool SOCBuy;
        private bool SOCSell;
        private bool MA_BiasBuy;
        private bool MA_BiasSell;

        protected override void OnStart()
        {
            _volumeInUnits = Symbol.QuantityToVolumeInUnits(VolumeInLots);
            MACDBuy = false;
            MACDSell = false;
            SOCBuy = false;
            SOCSell = false;
            MA_BiasBuy = false;
            MA_BiasSell = false;
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, MACDPeriod);
            _SOC = Indicators.StochasticOscillator(KPeriods, K_Slowing, DPeriods, MaType);
            _MA_50 = Indicators.ExponentialMovingAverage(Price, MA_Bias);
        }

        protected override void OnBar()
        {
            var ActiveBuy = Positions.Find(Label, SymbolName, TradeType.Buy);
            var ActiveSell = Positions.Find(Label, SymbolName, TradeType.Sell);

            // MACD cross
            if (_MACD.MACD.Last(1) < _MACD.Signal.Last(1) && _MACD.MACD.Last(0) > _MACD.Signal.Last(0) && _MACD.Signal.Last(0) < 0)
            {
                MACDBuy = true;
            }
            else if (_MACD.MACD.Last(1) > _MACD.Signal.Last(1) && _MACD.MACD.Last(0) < _MACD.Signal.Last(0) && _MACD.Signal.Last(0) > 0)
            {
                MACDSell = true;
            }

            // Stochastic cross
            if (_SOC.PercentK.Last(1) > _SOC.PercentD.Last(1) && _SOC.PercentK.Last(2) <= _SOC.PercentD.Last(2) && _SOC.PercentK.Last(1) <= 20)
            {
                SOCBuy = true;
            }
            else if (_SOC.PercentK.Last(1) < _SOC.PercentD.Last(1) && _SOC.PercentK.Last(2) >= _SOC.PercentD.Last(2) && _SOC.PercentK.Last(1) >= 80)
            {
                SOCSell = true;
            }
            // Moving Average Bias

            if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasBuy = true;
            }
            else if (Symbol.Bid > _MA_50.Result.LastValue)
            {
                MA_BiasSell = true;
            }

            if (MACDBuy == true && SOCBuy == true && MA_BiasBuy == true && ActiveBuy == null)
            {
                if (ActiveSell != null)
                {
                    ClosePosition(ActiveSell);
                }

                ExecuteMarketOrder(TradeType.Buy, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }

            if (MACDSell == true && SOCSell == true && MA_BiasSell == true && ActiveSell == null)
            {
                if (ActiveBuy != null)
                {
                    ClosePosition(ActiveBuy);
                }

                ExecuteMarketOrder(TradeType.Sell, SymbolName, _volumeInUnits, Label, StopLoss, TakeProfit);
            }
        }
    }
}

Before developing an indicator or cBot for cTrader please learn the C# basics and then read cTrader automate API references.


@amusleh