Category Other  Published on 08/05/2023

Bollinger Bands Trading Strategy

Description

Hey-ho! 

Let us introduce the cBot with a built-in BB indicator. This cool trading tool will help you to monitor price volatility.

 

Features

The BB strategy can:

  • Check ready-formed bars and current price position. You can select the right time to open a trade. 
  • Work in 3 modes: Current price and BB line crossing (Current Breakout), Closing of the last bar above/below the line (Close Above/Below), Price and the last crossed bar crossing (Bar Cross).
  • Manage positions. 
  • Calculate all the risks for the entire position.
  • Remove the Stop Loss level on the opening price after overcoming a certain distance thanks to the BreakEven option. In this case, your trade won’t be a loss-making one.

Note! You can select the BB line (Top or Bottom) and crossing type for every trade type. 

 

Parameters

We have equipped our EA with more than 14 additional parameters. So, you can configure the cBot according to your needs!

Note! Try the Bollinger Bands strategy on your demo account first before going live.


 

Other Products

Ichimoku Cloud System: 

PSAR Strategy: 

ADR Custom Indicator: 

Daily H/L Custom Indicator: 

 

Contact Info

Contact us via support@4xdev.com.

Check out our cozy Telegram blog for traders: https://t.me/Forexdev.  

Visit our website to find more tools and programming services: https://bit.ly/44BFRG3

Take a look at our YouTube channel: https://www.youtube.com/channel/UChsDb4Q8X2Vl5DJ7H8PzlHQ.   


 

Feel the heavenly pleasure of trading — with 4xDev.


 


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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class BbTradeSystem : Robot
    {
        public enum ENUM_TP_TYPE
        {
            Fixed = 0,
            RiskRatio = 1
        }
        public enum ENUM_RISK_SOURCE
        {
            Equity = 0,
            Balance = 1
        }

        public enum ENUM_LOT_TYPE
        {
            Fixed_Lot = 0,
            Percent = 1
        }
        public enum ENUM_CROSS_TYPE
        {
            Current_Breakout = 0,
            //Current breakout
            Close_Above_Below = 1,
            //Close Above/Below
            Bar_Cross = 2
            //Bar Cross
        }
        public enum ENUM_CROSS_LINE
        {
            Top_Line = 0,
            // Top Line
            Bottom_Line = 1
            // Bottom Line
        }
        public enum ENUM_CROSS_DIRECTION
        {
            Above = 0,
            // Above
            Below = 1
            // Below
        }

        #region Input BB Parameters

        [Parameter("BB Source", Group = "BB Parameters")]
        public DataSeries BBSeries { get; set; }

        [Parameter("BB Period", Group = "BB Parameters", DefaultValue = 20)]
        public int BBPeriods { get; set; }

        [Parameter("Bands Deviation", Group = "BB Parameters", DefaultValue = 2)]
        public double deviation { get; set; }

        [Parameter("MA Type", Group = "BB Parameters")]
        public MovingAverageType maType { get; set; }
        #endregion


        #region Input Trade Parameters

        [Parameter("Label", Group = "Trade Parameters", DefaultValue = "BB Trade System")]
        public string Label { get; set; }

        [Parameter("Cross Type", Group = "Trade Parameters", DefaultValue = ENUM_CROSS_TYPE.Current_Breakout)]
        public ENUM_CROSS_TYPE crossType { get; set; }
        [Parameter("Buy Cross Line", Group = "Trade Parameters", DefaultValue = ENUM_CROSS_LINE.Bottom_Line)]
        public ENUM_CROSS_LINE crossLineBuy { get; set; }
        [Parameter("Buy Cross Direction", Group = "Trade Parameters", DefaultValue = ENUM_CROSS_DIRECTION.Below)]
        public ENUM_CROSS_DIRECTION crossDirectBuy { get; set; }
        [Parameter("Sell Cross Line", Group = "Trade Parameters", DefaultValue = ENUM_CROSS_LINE.Top_Line)]
        public ENUM_CROSS_LINE crossLineSell { get; set; }
        [Parameter("Sell Cross Direction", Group = "Trade Parameters", DefaultValue = ENUM_CROSS_DIRECTION.Above)]
        public ENUM_CROSS_DIRECTION crossDirectSell { get; set; }

        [Parameter("Take Profit type", Group = "Trade Parameters", DefaultValue = ENUM_TP_TYPE.Fixed)]
        public ENUM_TP_TYPE tpType { get; set; }

        [Parameter("Stop Loss in pips", Group = "Trade Parameters", DefaultValue = 0)]
        public double SL { get; set; }

        [Parameter("Take Profit value", Group = "Trade Parameters", DefaultValue = 0)]
        public double TP { get; set; }

        [Parameter("Close on the opposite signal", Group = "Trade Parameters", DefaultValue = true)]
        public bool oppositeClose { get; set; }

        [Parameter("Max Orders", Group = "Trade Parameters", DefaultValue = 1)]
        public int maxOrders { get; set; }

        [Parameter("Use Reverse Trade", Group = "Trade Parameters", DefaultValue = true)]
        public bool reverseTrade { get; set; }

        #endregion
        #region Input Lot Size Parameters
        [Parameter("Lot Type", Group = "Lot Size", DefaultValue = ENUM_LOT_TYPE.Fixed_Lot)]
        public ENUM_LOT_TYPE lotType { get; set; }

        [Parameter("Risk Source", Group = "Lot Size", DefaultValue = ENUM_RISK_SOURCE.Balance)]
        public ENUM_RISK_SOURCE riskSource { get; set; }

        [Parameter("Risk/Lot Value", Group = "Lot Size", DefaultValue = 0.1)]
        public double risk { get; set; }
        #endregion
        #region Input Break Even Parameters
        [Parameter("Use BreakEven", Group = "BreakEven", DefaultValue = false)]
        public bool UseBE { get; set; }
        [Parameter("BreakEven Start(pips)", Group = "BreakEven", DefaultValue = 10)]
        public double BEStart { get; set; }

        [Parameter("BreakEven Profit(pips)", Group = "BreakEven", DefaultValue = 0)]
        public double BEProfit { get; set; }
        #endregion

        private BollingerBands BB;

        protected override void OnStart()
        {
            BB = Indicators.BollingerBands(BBSeries, BBPeriods, deviation, maType);
            // Put your initialization logic here
        }

        double bbPrevTop;
        double bbPrevBott;
        double pricePrev;
        DateTime lastTrade;


        protected override void OnTick()
        {
            if (UseBE)
                BreakEven();

            if (lastTrade != Bars.OpenTimes.Last(0))
            {
                if (CheckCondition(TradeType.Buy))
                {
                    if (oppositeClose)
                        CloseOrders(TradeType.Sell);
                    if(CalculateOrders() < maxOrders)
                    {
                        lastTrade = Bars.OpenTimes.Last(0);
                        OpenOrder(TradeType.Buy);
                    }
                }
                if (CheckCondition(TradeType.Sell))
                {
                    if (oppositeClose)
                        CloseOrders(TradeType.Buy);
                    if(CalculateOrders() < maxOrders)
                    {
                        lastTrade = Bars.OpenTimes.Last(0);
                        OpenOrder(TradeType.Sell);
                    }
                }
            }
            bbPrevTop = BB.Top.Last(0);
            bbPrevBott = BB.Bottom.Last(0);
            pricePrev = Bars.ClosePrices.Last(0);

        }

        bool CheckOrder(TradeType type)
        {
            if (Positions.FindAll(Label, Symbol, type) != null)
                return false;
            return true;
        }

        int CalculateOrders()
        {
            return Positions.FindAll(Label, Symbol).Length;
        }

        void CloseOrders(TradeType type)
        {
            if (reverseTrade)
                type = type == TradeType.Buy ? TradeType.Sell : TradeType.Buy;

            foreach (var pos in Positions.FindAll(Label, Symbol, type))
            {
                ClosePosition(pos);
            }
        }

        void OpenOrder(TradeType type)
        {
            if (reverseTrade)
                type = type == TradeType.Buy ? TradeType.Sell : TradeType.Buy;

            double op;
            double tp = tpType == ENUM_TP_TYPE.Fixed ? TP : SL * TP;
            double sl;

            double source = riskSource == ENUM_RISK_SOURCE.Balance ? Account.Balance : Account.Equity;

            double volumeInUnits = 0;
            if (lotType == ENUM_LOT_TYPE.Fixed_Lot)
                volumeInUnits = Symbol.QuantityToVolumeInUnits(risk);
            else
                volumeInUnits = CalculateVolume(SL, risk, source);

            if (volumeInUnits == -1)
                return;
            ExecuteMarketOrder(type, SymbolName, volumeInUnits, Label, SL, TP);
        }


        private void BreakEven()
        {
            if (!UseBE)
                return;

            foreach (var pos in Positions.FindAll(Label, SymbolName))
            {
                if (pos.TradeType == TradeType.Buy)
                {
                    if (Symbol.Ask >= pos.EntryPrice + BEStart * Symbol.PipSize && (pos.StopLoss < pos.EntryPrice + BEProfit * Symbol.PipSize || pos.StopLoss == null))
                    {
                        ModifyPosition(pos, pos.EntryPrice + BEProfit * Symbol.PipSize, pos.TakeProfit);
                    }
                }
                if (pos.TradeType == TradeType.Sell)
                {
                    if (Symbol.Bid <= pos.EntryPrice - BEStart * Symbol.PipSize && (pos.StopLoss > pos.EntryPrice - BEProfit * Symbol.PipSize || pos.StopLoss == null))
                    {
                        ModifyPosition(pos, pos.EntryPrice + BEProfit * Symbol.PipSize, pos.TakeProfit);
                    }
                }
            }
        }

        bool CheckCondition(TradeType trade)
        {
            int shift = crossType == ENUM_CROSS_TYPE.Current_Breakout ? 0 : 1;

            double bb;
            double bbPrev;

            if (trade == TradeType.Buy)
            {
                if (crossLineBuy == ENUM_CROSS_LINE.Top_Line)
                    bb = BB.Top.Last(shift);
                else
                    bb = BB.Bottom.Last(shift);
                if (crossType == ENUM_CROSS_TYPE.Current_Breakout)
                {
                    if (crossLineBuy == ENUM_CROSS_LINE.Top_Line)
                        bbPrev = bbPrevTop;
                    else
                        bbPrev = bbPrevBott;
                    if (crossDirectBuy == ENUM_CROSS_DIRECTION.Above)
                    {
                        if (Bars.ClosePrices.Last(0) < bb && pricePrev > bbPrev)
                            return true;
                        else
                            return false;
                    }
                    if (crossDirectBuy == ENUM_CROSS_DIRECTION.Below)
                    {
                        if (Bars.ClosePrices.Last(0) > bb && pricePrev < bbPrev)
                            return true;
                        else
                            return false;
                    }
                }
                if (crossType == ENUM_CROSS_TYPE.Close_Above_Below)
                {
                    if (crossDirectBuy == ENUM_CROSS_DIRECTION.Above)
                    {
                        if (Bars.ClosePrices.Last(1) > bb)
                            return true;
                        else
                            return false;
                    }
                    if (crossDirectBuy == ENUM_CROSS_DIRECTION.Below)
                    {
                        if (Bars.ClosePrices.Last(1) < bb)
                            return true;
                        else
                            return false;
                    }
                }
                if (crossType == ENUM_CROSS_TYPE.Bar_Cross)
                {
                    if (Bars.LowPrices.Last(1) < bb && Bars.HighPrices.Last(1) > bb)
                        return true;
                    else
                        return false;
                }
            }


            if (trade == TradeType.Sell)
            {
                if (crossLineSell == ENUM_CROSS_LINE.Top_Line)
                    bb = BB.Top.Last(shift);
                else
                    bb = BB.Bottom.Last(shift);
                if (crossType == ENUM_CROSS_TYPE.Current_Breakout)
                {
                    if (crossLineSell == ENUM_CROSS_LINE.Top_Line)
                        bbPrev = bbPrevTop;
                    else
                        bbPrev = bbPrevBott;
                    if (crossDirectSell == ENUM_CROSS_DIRECTION.Above)
                    {
                        if (Bars.ClosePrices.Last(0) < bb && pricePrev > bbPrev)
                            return true;
                        else
                            return false;
                    }
                    if (crossDirectSell == ENUM_CROSS_DIRECTION.Below)
                    {
                        if (Bars.ClosePrices.Last(0) > bb && pricePrev < bbPrev)
                            return true;
                        else
                            return false;
                    }
                }
                if (crossType == ENUM_CROSS_TYPE.Close_Above_Below)
                {
                    if (crossDirectSell == ENUM_CROSS_DIRECTION.Above)
                    {
                        if (Bars.ClosePrices.Last(1) > bb)
                            return true;
                        else
                            return false;
                    }
                    if (crossDirectSell == ENUM_CROSS_DIRECTION.Below)
                    {
                        if (Bars.ClosePrices.Last(1) < bb)
                            return true;
                        else
                            return false;
                    }
                }
                if (crossType == ENUM_CROSS_TYPE.Bar_Cross)
                {
                    if (Bars.LowPrices.Last(1) < bb && Bars.HighPrices.Last(1) > bb)
                        return true;
                    else
                        return false;
                }
            }
            return false;
        }
        private double CalculateVolume(double stopLossPips, double riskSize, double source)
        {
            // source = Account.Balance or Account.Equity
            double riskPerTrade = source * riskSize / 100;
            double totalPips = stopLossPips;

            double _volume;
            double exactVolume = riskPerTrade / (Symbol.PipValue * totalPips);
            if (exactVolume >= Symbol.VolumeInUnitsMin)
            {
                _volume = Symbol.NormalizeVolumeInUnits(exactVolume);
            }
            else
            {
                _volume = -1;
                Print("Not enough Equity to place minimum trade, exactVolume " + exactVolume + " is not >= Symbol.VolumeInUnitsMin " + Symbol.VolumeInUnitsMin);
            }
            return _volume;
        }
        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
    }
}


4xDev's avatar
4xDev

Joined on 11.12.2019

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: BB_Trade_System.algo
  • Rating: 3.33
  • Installs: 2485
  • Modified: 05/12/2022 12:29
Comments
Log in to add a comment.
RO
rosscortb · 2 months ago

This doesn't work, position never closes out

rollopack's avatar
rollopack · 1 year ago

Thanks for the bot, from the tests it seems very valid.
Using the fpmarkets broker is giving me some warnings:

13/10/2023 13:54:16.565 | → Request to amend position PID2192082 (SL: 158.580) is REJECTED with error "New SL for BUY position should be <= current BID price. current BID: 157.508, SL: 158.58;"
13/10/2023 12:30:02.635 | → Request to amend position PID2169355 is REJECTED with error "Nothing to amend."

In the config the Stop Loss is set to 0

AT
atks381a · 1 year ago

Hi,

trank you for your cbot.

Please, can you tell me where can i find a manual of your Bollinger bands Tranding Strategy cbot explaining the different parameters how they affect the opening of the position?

 

Thnak you again

4xDev's avatar
4xDev · 2 years ago

@TheMC, we found the problem. The parameter does not work if the maximum number of orders is 1.
We have uploaded the fixed version of the tool.

4xDev's avatar
4xDev · 2 years ago

@TheMC Let us check this problem. We will solve this problem asap. 

4xDev's avatar
4xDev · 2 years ago

If you want to create a custom tool, please contact us via support@4xdev.com. Or check our website 4xdev(dot)com.

TH
TheMC · 2 years ago

Hi,
congratulations for the excellent work.
However, it seems to me that the bot does not close positions at the opposite signal.