Standard Deviation "revert to mean" Bot

Created at 14 Jun 2022, 20:01
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!
KU

kurtisnauss

Joined 08.06.2022

Standard Deviation "revert to mean" Bot
14 Jun 2022, 20:01


Hi, 

I am interested in creating a bot that would enter both short and long positions based on a deviation away from a mean.

This is where I am so far... I am unsure how to get it read when it reaches a specified amount of deviations above or below the moving average.

Is there an indicator that can be applied into the parameters that will track this that I can optimize to being anywhere between 2 - 4 standard deviations away?

I would like it to enter short when it reaches a set distance above the moving average, and then long when it reaches a set distance below the moving average. Then have it exit either when it reverts back to that mean, or possibly (if it optimizes better this way) to have it exit when it reaches back to a specified standard deviation away. 

 

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.EasternStandardTime, AccessRights = AccessRights.None)]
    public class MADeviationBot : Robot
    {    
        [Parameter("Instance Name", DefaultValue = "")]
        public string InstanceName { get; set; }
    
        [Parameter("Source")]
        public DataSeries SourceSeries { get; set; }        

        [Parameter("MA Type")]
        public MovingAverageType Type { get; set; }

        [Parameter("MA Period")]
        public int MAPeriod { get; set; }

        [Parameter("Stop Loss")]
        public int StopLoss { get; set; }

        [Parameter("Risk %", MinValue = 0.01, Step = 0.01)]
        public double RiskPerTrade { get; set; }

        private MovingAverage MAtype;

        protected override void OnStart()
        {
            MAtype = Indicators.MovingAverage(SourceSeries, MAPeriod, Type);
        }

        protected override void OnBar()
        {
            int index = Bars.Count - 1;
            Entry(index);
            Exits(index);
        }

        private void Entry(int index)
        {
            if (Bars.ClosePrices[index] < //-4 or optimized Standard Deviation below MAtype.Result )
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, GetVolume(StopLoss), InstanceName, StopLoss, null);
                }
            }

            if (Bars.ClosePrices[index] > //+4 or optimized Standard Deviation above MAtype.Result )
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, GetVolume(StopLoss), InstanceName, StopLoss, null);
                }
            }
        }

        private void Exits(int index)
        {
            var positions = Positions.FindAll(InstanceName, SymbolName);

            foreach (var position in positions)

                if ((position.TradeType == TradeType.Buy && Bars.ClosePrices[index] > MAtype.Result) || (position.TradeType == TradeType.Sell && Bars.ClosePrices[index] < Matype.Result.Result[index]))

                    ClosePosition(position);
        }

        private double GetVolume(double? stopLossPips = null)
        {
            double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;

            // Change this to Account.Equity if you want to
            double baseNumber = Account.Balance;

            double sizeInLots = Math.Round((baseNumber * RiskPerTrade / 100) / (stopLossPips.Value * costPerPip), 1);

            var result = Symbol.QuantityToVolumeInUnits(sizeInLots);

            return result;
        }
    }
}

 


@kurtisnauss
Replies

amusleh
15 Jun 2022, 08:09

H,

You can use standard deviation indicator and a multiplier number like Bollinger Bands, example:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.EasternStandardTime, AccessRights = AccessRights.None)]
    public class MADeviationBot : Robot
    {
        [Parameter("Instance Name", DefaultValue = "")]
        public string InstanceName { get; set; }

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

        [Parameter("MA Type")]
        public MovingAverageType Type { get; set; }

        [Parameter("MA Period")]
        public int MAPeriod { get; set; }

        [Parameter("Stop Loss")]
        public int StopLoss { get; set; }

        [Parameter("Risk %", MinValue = 0.01, Step = 0.01)]
        public double RiskPerTrade { get; set; }

        [Parameter("Std Multiplier", DefaultValue = 1)]
        public double StdMultiplier { get; set; }

        private MovingAverage _ma;

        private StandardDeviation _standardDeviation;

        protected override void OnStart()
        {
            _ma = Indicators.MovingAverage(SourceSeries, MAPeriod, Type);
            _standardDeviation = Indicators.StandardDeviation(SourceSeries, MAPeriod, Type);
        }

        protected override void OnBar()
        {
            int index = Bars.Count - 1;
            Entry(index);
            Exits(index);
        }

        private void Entry(int index)
        {
            if (_ma.Result[index] - Bars.ClosePrices[index] > _standardDeviation.Result[index] * StdMultiplier)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, GetVolume(StopLoss), InstanceName, StopLoss, null);
            }
            else if (Bars.ClosePrices[index] - _ma.Result[index] > _standardDeviation.Result[index] * StdMultiplier)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, GetVolume(StopLoss), InstanceName, StopLoss, null);
            }
        }

        private void Exits(int index)
        {
            var positions = Positions.FindAll(InstanceName, SymbolName);

            foreach (var position in positions)
            {
                if ((position.TradeType == TradeType.Buy && Bars.ClosePrices[index] > _ma.Result[index]) || (position.TradeType == TradeType.Sell && Bars.ClosePrices[index] < _ma.Result[index]))
                {
                    ClosePosition(position);
                }
            }
        }

        private double GetVolume(double? stopLossPips = null)
        {
            double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;

            // Change this to Account.Equity if you want to
            double baseNumber = Account.Balance;

            double sizeInLots = Math.Round((baseNumber * RiskPerTrade / 100) / (stopLossPips.Value * costPerPip), 1);

            var result = Symbol.QuantityToVolumeInUnits(sizeInLots);

            return result;
        }
    }
}

 


@amusleh

kurtisnauss
15 Jun 2022, 15:39

RE: Thank you, I will try that out and play around with it. There are other parameters I would like to add but needed that before anything.

amusleh said:

H,

You can use standard deviation indicator and a multiplier number like Bollinger Bands, example:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.EasternStandardTime, AccessRights = AccessRights.None)]
    public class MADeviationBot : Robot
    {
        [Parameter("Instance Name", DefaultValue = "")]
        public string InstanceName { get; set; }

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

        [Parameter("MA Type")]
        public MovingAverageType Type { get; set; }

        [Parameter("MA Period")]
        public int MAPeriod { get; set; }

        [Parameter("Stop Loss")]
        public int StopLoss { get; set; }

        [Parameter("Risk %", MinValue = 0.01, Step = 0.01)]
        public double RiskPerTrade { get; set; }

        [Parameter("Std Multiplier", DefaultValue = 1)]
        public double StdMultiplier { get; set; }

        private MovingAverage _ma;

        private StandardDeviation _standardDeviation;

        protected override void OnStart()
        {
            _ma = Indicators.MovingAverage(SourceSeries, MAPeriod, Type);
            _standardDeviation = Indicators.StandardDeviation(SourceSeries, MAPeriod, Type);
        }

        protected override void OnBar()
        {
            int index = Bars.Count - 1;
            Entry(index);
            Exits(index);
        }

        private void Entry(int index)
        {
            if (_ma.Result[index] - Bars.ClosePrices[index] > _standardDeviation.Result[index] * StdMultiplier)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, GetVolume(StopLoss), InstanceName, StopLoss, null);
            }
            else if (Bars.ClosePrices[index] - _ma.Result[index] > _standardDeviation.Result[index] * StdMultiplier)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, GetVolume(StopLoss), InstanceName, StopLoss, null);
            }
        }

        private void Exits(int index)
        {
            var positions = Positions.FindAll(InstanceName, SymbolName);

            foreach (var position in positions)
            {
                if ((position.TradeType == TradeType.Buy && Bars.ClosePrices[index] > _ma.Result[index]) || (position.TradeType == TradeType.Sell && Bars.ClosePrices[index] < _ma.Result[index]))
                {
                    ClosePosition(position);
                }
            }
        }

        private double GetVolume(double? stopLossPips = null)
        {
            double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;

            // Change this to Account.Equity if you want to
            double baseNumber = Account.Balance;

            double sizeInLots = Math.Round((baseNumber * RiskPerTrade / 100) / (stopLossPips.Value * costPerPip), 1);

            var result = Symbol.QuantityToVolumeInUnits(sizeInLots);

            return result;
        }
    }
}

 

 


@kurtisnauss