Category Range  Published on 22/11/2015

Daily Candle Breakout

Description

Find uploaded an updated version of the cbot. It has slightly modified logic, RSI filtering property and some more parameters to modify. Also a bidirectional trailing stop has been added (both positive and negative triggers allowed). Backtesting ability has been improved as well. Tested on USDCAD, 2 out of 3 winning trades on average.

Some less self-explanatory parameters explained:

Equity Risk Value - percentage of available equity as a parameter of trade volume (in %), use values of consecutive tens 10, 20, 30 ... and so on.

Breakout Trigger In Pips - number of pips a new bar has to break the previous range for a position to be opened.

Below equity curve based on trades spanning over 3 years of data. Currency pair - USDCAD. Starting balance - 1k EUR.

 

..........................................................................................................................................................................................

This robot trades a very simple strategy of daily bar breakouts. You can read more about it HERE . I am new to programming so expect nothing more than you would do from an experiment. Structural nuances of the code are based on work of nobulart .

If you want to backtest this robot be sure to change Backtesting Properties of Market Data to "Tick data from Server (accurate)", otherwise number of trade entries will be missed out.

Do fidle with the params in optimization, however, if you're lazy the default ones should give decent results. To unclutter the Log just use comment characters "//" with appropriate lines of code.

Comments and improvement ideas greatly appreciated.

 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Do not use on live accounts. This is an experimental algorithm and no results are guaranteed. Losses may exceed an initial deposit.

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


// Daily Candle Breakout ver. 1.1
// Robot created by Qbson, Nov 2015
// Use with demo account only!

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

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class DailyCandleBreakout : Robot
    {
        [Parameter("Robot No", DefaultValue = 1, MinValue = 0)]
        public int RobotNo { get; set; }

        // Position volume as percentage of equity risk
        [Parameter("Equity Risk Value", DefaultValue = 30, MinValue = 0)]
        public double equityRisk { get; set; }

        // Stop Loss in pipis
        [Parameter("Stop Loss", DefaultValue = 65, MinValue = 0)]
        public int StopLoss { get; set; }

        // Defines relevant bar minimal size
        [Parameter("Min Bar Size", DefaultValue = 66, MinValue = 1)]
        public int minBarSize { get; set; }

        // Defines minimum candle to shadow ratio
        [Parameter("Min Candle To Shadow Ratio", DefaultValue = 0.23, MinValue = 0, MaxValue = 1)]
        public double csMinRatio { get; set; }

        // Breakout threshold in pips
        [Parameter("Breakout Trigger In Pips", DefaultValue = 9, MinValue = 0)]
        public int breakoutPips { get; set; }

        // Disables trading against the trend
        [Parameter("WMA Filtering", DefaultValue = false)]
        public bool wmaBias { get; set; }

        // Weighted Moving Average smoothing
        [Parameter("WMA Period", DefaultValue = 150, MinValue = 1)]
        public int wmaPeriod { get; set; }

        // Disables overbought / oversold trades
        [Parameter("RSI Filtering", DefaultValue = false)]
        public bool rsiBias { get; set; }

        // RSI period
        [Parameter("RSI Period", DefaultValue = 14, MinValue = 1)]
        public int rsiPeriod { get; set; }

        // RSI Cut Off value - overbought
        [Parameter("RSI Overbought", DefaultValue = 73, MinValue = 1)]
        public int overbought { get; set; }

        // RSI Cut Off value - oversold
        [Parameter("RSI Oversold", DefaultValue = 33, MinValue = 1)]
        public int oversold { get; set; }

        // Disables trading in volatile market
        [Parameter("ATR Filtering", DefaultValue = false)]
        public bool atrBias { get; set; }

        // ATR bar period
        [Parameter("ATR Period", DefaultValue = 21, MinValue = 1)]
        public int atrPeriod { get; set; }

        // ATR MA type
        [Parameter("ATR MA Type", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType maType { get; set; }

        // ATR Cut Off Value
        [Parameter("ATR Filter", DefaultValue = 1, MinValue = 0.0)]
        public double atrMax { get; set; }

        // Enables trailing stops
        [Parameter("Trailing Stops", DefaultValue = false)]
        public bool TrailingStops { get; set; }

        [Parameter("Trailing Stop Trigger (pips)", DefaultValue = 500)]
        public int TrailingStopTrigger { get; set; }

        [Parameter("Trailing Stop Distance (pips)", DefaultValue = 500)]
        public int TrailingStopDistance { get; set; }

        // Equity Protection
        [Parameter("Equity Stop Loss (ratio)", DefaultValue = 0.2, MinValue = 0, MaxValue = 1)]
        public double EquityStop { get; set; }

        // Enables trade volume doubling when a stop loss is hit
        [Parameter("Martingale Enabled", DefaultValue = false)]
        public bool Martingale { get; set; }

        // Maximum simultaneous long positions
        [Parameter("Long Trades", DefaultValue = 1, MinValue = 0)]
        public int MaxLongTrades { get; set; }

        // Maximum simultaneous short positions
        [Parameter("Short Trades", DefaultValue = 1, MinValue = 0)]
        public int MaxShortTrades { get; set; }

        private int LongPositions = 0, ShortPositions = 0, MaxLong = 0, MaxShort = 0;
        private double BuyVolume = 0, accBalance = 0, latestVolume = 0;
        private double TakeProfit = 0, longNewTP = 0, longNewSL = 0, shortNewTP = 0, shortNewSL = 0;
        private double bid = 0, ask = 0, pipsize = 0, OpeningBalance = 0;
        private double breakoutShortTrigger = 0, breakoutLongTrigger = 0, breakoutShortThreshold = 0, breakoutLongThreshold = 0;

        private double cb_open = 0;
        private double b_open = 0, b_close = 0, s_min = 0, s_max = 0;
        private double pb_open = 0, pb_close = 0, ps_min = 0, ps_max = 0;
        private double sSize = 0, psSize = 0, cSize = 0, csRatio = 0;

        private int LongSinceOpen = 0, ShortSinceOpen = 0;

        private int MartingaleActive = 0;

        private bool BuySafe = true, SellSafe = true, BuySafeRSI = true, SellSafeRSI = true, TradeSafe = true, InsideBar = false, isTrigerred = false, DailyPositionsLimit = false;

        private long tradeVolume = 0;

        private WeightedMovingAverage ind_wma;
        private AverageTrueRange ind_atr;
        private RelativeStrengthIndex ind_rsi;

        private string RobotID;

        protected override void OnStart()
        {
            RobotID = "Trading robot " + RobotNo + " - " + Symbol.Code;

            Positions.Closed += PositionsOnClosed;

            OpeningBalance = Account.Balance;

            BuyVolume = Math.Ceiling(OpeningBalance / 100) * 100 * equityRisk;

            ind_wma = Indicators.WeightedMovingAverage(MarketSeries.Close, wmaPeriod);
            ind_rsi = Indicators.RelativeStrengthIndex(MarketData.GetSeries(TimeFrame.Hour4).Close, rsiPeriod);
            ind_atr = Indicators.AverageTrueRange(atrPeriod, maType);

            MaxLong = MaxLongTrades;
            MaxShort = MaxShortTrades;

            // Identifying opened positions
            foreach (var position in Positions)
            {
                if (position.Label == RobotID)
                    switch (position.TradeType)
                    {
                        case TradeType.Buy:
                            LongPositions++;
                            break;
                        case TradeType.Sell:
                            ShortPositions++;
                            break;
                    }
            }
            Print("Robot at work");
        }

        protected override void OnBar()
        {
            DailyPositionsLimit = false;

            pipsize = Symbol.PipSize;

            SellSafeRSI = true;
            BuySafeRSI = true;

            // Getting current bar specs
            var index0 = MarketSeries.Close.Count - 1;
            cb_open = (double)MarketSeries.Open[index0];

            // Getting latest bar spec
            var index1 = MarketSeries.Close.Count - 2;
            b_open = (double)MarketSeries.Open[index1];
            b_close = (double)MarketSeries.Close[index1];
            s_min = (double)MarketSeries.Low[index1];
            s_max = (double)MarketSeries.High[index1];

            // Getting previous bar spec
            var index2 = MarketSeries.Close.Count - 3;
            pb_open = (double)MarketSeries.Open[index2];
            pb_close = (double)MarketSeries.Close[index2];
            ps_min = (double)MarketSeries.Low[index2];
            ps_max = (double)MarketSeries.High[index2];

            // Latest candle shadow size and ratio calculation
            sSize = (double)((s_max - s_min) / pipsize);
            cSize = (double)(Math.Abs(b_open - b_close) / pipsize);
            csRatio = (double)(cSize / sSize);

            // Previous candle size calculation
            psSize = (double)((ps_max - ps_min) / pipsize);

            if (s_max < ps_max && s_min > ps_min)
                InsideBar = true;
            else
                InsideBar = false;

            //Print("Bar size - {0}, Candle/shadow ratio - {1}, Inside Bar - {2}", sSize, csRatio, InsideBar);
            //Print("Previous candle o - {0}, h - {1}, l - {2}, c - {3}", pb_open, ps_max, ps_min, pb_close);
            //Print("Latest candle o - {0}, h - {1}, l - {2}, c - {3}", b_open, s_max, s_min, b_close);

            foreach (var position in Positions)
            {
                if (position.Label == RobotID)
                    switch (position.TradeType)
                    {
                        case TradeType.Buy:
                            LongSinceOpen++;
                            Print("Long position opened {0} bars ago", LongSinceOpen);
                            break;
                        case TradeType.Sell:
                            ShortSinceOpen++;
                            Print("Short position opened {0} bars ago", ShortSinceOpen);
                            break;
                    }
            }

            if (LongSinceOpen > 0)
            {
                longNewTP = (double)s_max + (0.3 * (Math.Abs(sSize)) * pipsize);
                longNewSL = (double)s_min - (0.3 * (Math.Abs(sSize)) * pipsize);
                ModifyLongPosition(longNewSL, longNewTP);
            }

            if (ShortSinceOpen > 0)
            {
                shortNewSL = (double)s_max + (0.3 * (Math.Abs(sSize)) * pipsize);
                shortNewTP = (double)s_min - (0.3 * (Math.Abs(sSize)) * pipsize);
                ModifyShortPosition(shortNewSL, shortNewTP);
            }
        }

        private void ModifyLongPosition(double stop, double take)
        {
            foreach (var position in Positions)
                if (position.Label == RobotID)
                {
                    ModifyPosition(position, stop, take);
                    //Print("Long position modified SL - {0}, TP - {1}", longNewSL, longNewTP);
                }
        }

        private void ModifyShortPosition(double stop, double take)
        {
            foreach (var position in Positions)
                if (position.Label == RobotID)
                {
                    ModifyPosition(position, stop, take);
                    //Print("Short position modified SL - {0}, TP - {1}", shortNewSL, shortNewTP);
                }
        }

        protected override void OnTick()
        {
            breakoutShortTrigger = (double)(s_min - breakoutPips * Symbol.PipSize);
            breakoutLongTrigger = (double)(s_max + breakoutPips * Symbol.PipSize);

            breakoutShortThreshold = (double)(breakoutShortTrigger - breakoutPips * Symbol.PipSize);
            breakoutLongThreshold = (double)(breakoutLongTrigger + breakoutPips * Symbol.PipSize);

            bid = Symbol.Bid;
            ask = Symbol.Ask;

            BuySafe = true;
            SellSafe = true;
            TradeSafe = true;

            if (Account.Equity / Account.Balance < EquityStop)
            {
                Print("Equity protection stop triggered. All positions closed.");
                ClosePositions();
                Stop();
            }

            if (wmaBias == true)
            {
                if (bid > ind_wma.Result[MarketSeries.WeightedClose.Count - 2])
                    SellSafe = false;

                if (ask < ind_wma.Result[MarketSeries.WeightedClose.Count - 2])
                    BuySafe = false;
            }

            if (rsiBias == true)
            {
                if (ind_rsi.Result.LastValue < oversold)
                {
                    SellSafeRSI = false;
                    //Print("RSI sell unsafe");
                }

                if (ind_rsi.Result.LastValue > overbought)
                {
                    BuySafeRSI = false;
                    //Print("RSI buy unsafe");
                }
            }

            if (atrBias == true)
            {
                if (atrMax < ind_atr.Result.LastValue)
                    TradeSafe = false;
            }

            if (TrailingStops)
                AdjustTrailingStops();

            if (TradeSafe == true && BuySafe == true && BuySafeRSI == true && LongPositions < MaxLong && sSize > minBarSize && csRatio > csMinRatio && InsideBar == false && ask >= breakoutLongTrigger && ask <= breakoutLongThreshold && DailyPositionsLimit == false)
            {
                OpenPosition(TradeType.Buy, BuyVolume);
                DailyPositionsLimit = true;
                Print("RSI value - {0}", ind_rsi.Result.LastValue);
            }

            if (TradeSafe == true && SellSafe == true && SellSafeRSI == true && ShortPositions < MaxShort && sSize > minBarSize && csRatio > csMinRatio && InsideBar == false && bid <= breakoutShortTrigger && bid <= breakoutShortThreshold && DailyPositionsLimit == false)
            {
                OpenPosition(TradeType.Sell, BuyVolume);
                DailyPositionsLimit = true;
                Print("RSI value - {0}", ind_rsi.Result.LastValue);
            }
        }

        private void OpenPosition(TradeType tradetype, double quantity)
        {
            switch (tradetype)
            {
                case TradeType.Buy:
                    TakeProfit = (int)sSize;
                    LongPositions++;
                    Print("Opened LONG position {0} of {1}", LongPositions, MaxLong);
                    break;
                case TradeType.Sell:
                    TakeProfit = (int)sSize;
                    ShortPositions++;
                    Print("Opened SHORT position {0} of {1}, take profit - {2}", ShortPositions, MaxShort, TakeProfit);
                    break;
            }

            //if (MartingaleActive > 0)
            //    TakeProfit = (int)(StopLoss / 2);

            tradeVolume = (long)quantity;

            ExecuteMarketOrder(tradetype, Symbol, tradeVolume, RobotID, StopLoss, TakeProfit);
        }

        private void ClosePositions()
        {
            foreach (var position in Positions)
            {
                if (position.Label == RobotID)
                    ClosePosition(position);
            }
        }

        private void PositionsOnClosed(PositionClosedEventArgs args)
        {
            var position = args.Position;
            switch (position.TradeType)
            {
                case TradeType.Buy:
                    LongPositions--;
                    Print("Closed LONG position. {0} of {1} remain open.", LongPositions, MaxLong);
                    LongSinceOpen = 0;
                    break;
                case TradeType.Sell:
                    ShortPositions--;
                    Print("Closed SHORT position. {0} of {1} remain open.", ShortPositions, MaxShort);
                    ShortSinceOpen = 0;
                    break;
            }

            accBalance = (double)Account.Balance;

            latestVolume = position.Volume;

            if (Martingale)
            {
                if (position.GrossProfit < 0)
                {
                    MartingaleActive++;
                    BuyVolume = latestVolume * 2;
                }
                else if (MartingaleActive == 0)
                    BuyVolume = Math.Ceiling(accBalance / 100) * 100 * equityRisk;
                else if (MartingaleActive > 0)
                {
                    MartingaleActive--;

                    if ((BuyVolume / 2) < (Math.Ceiling(accBalance / 100) * 100 * equityRisk))
                        BuyVolume = Math.Ceiling(accBalance / 100) * 100 * equityRisk;
                    else
                        BuyVolume = (int)(BuyVolume / 2);
                }
            }
            else
                BuyVolume = Math.Ceiling(accBalance / 100) * 100 * equityRisk;

            Print("Long {0} of {1}, Short {2} of {3}, Volume - {4}", LongPositions, MaxLong, ShortPositions, MaxShort, BuyVolume);
        }

        private void AdjustTrailingStops()
        {
            foreach (var position in Positions)
            {
                if (position.Label == RobotID)
                {
                    if (TrailingStopTrigger >= 0)
                    {
                        if (position.TradeType == TradeType.Buy)
                        {
                            double distance = Symbol.Bid - position.EntryPrice;

                            if (distance >= TrailingStopTrigger * Symbol.PipSize)
                            {
                                if (!isTrigerred)
                                {
                                    isTrigerred = true;
                                    Print("Trailing stop loss triggered on position {0}", position.Id);
                                }

                                double newStopLossPrice = Math.Round(Symbol.Bid - TrailingStopDistance * Symbol.PipSize, Symbol.Digits);

                                if (position.StopLoss == null || newStopLossPrice > position.StopLoss)
                                {
                                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                                    //Print("Trigger at - {0}, New SL - {1}, distance - {2}", TrailingStopTrigger * Symbol.PipSize, newStopLossPrice, distance);
                                }
                            }
                        }
                        else
                        {
                            double distance = position.EntryPrice - Symbol.Ask;

                            if (distance >= TrailingStopTrigger * Symbol.PipSize)
                            {
                                if (!isTrigerred)
                                {
                                    isTrigerred = true;
                                    Print("Trailing stop loss triggered on position {0}", position.Id);
                                }

                                double newStopLossPrice = Math.Round(Symbol.Ask + TrailingStopDistance * Symbol.PipSize, Symbol.Digits);

                                if (position.StopLoss == null || newStopLossPrice < position.StopLoss)
                                {
                                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                                    //Print("Trigger at - {0}, New SL - {1}, distance - {2}", TrailingStopTrigger * Symbol.PipSize, newStopLossPrice, distance);
                                }
                            }
                        }
                    }
                    else
                    {
                        if (position.TradeType == TradeType.Buy)
                        {
                            double distance = Symbol.Bid - position.EntryPrice;

                            if (distance <= TrailingStopTrigger * Symbol.PipSize)
                            {
                                if (!isTrigerred)
                                {
                                    isTrigerred = true;
                                    Print("Trailing stop loss triggered on position {0}", position.Id);
                                }

                                double newStopLossPrice = Math.Round(Symbol.Bid - TrailingStopDistance * Symbol.PipSize, Symbol.Digits);

                                if (position.StopLoss == null || newStopLossPrice > position.StopLoss)
                                {
                                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                                    Print("Trigger at - {0}, New SL - {1}, distance - {2}", TrailingStopTrigger * Symbol.PipSize, newStopLossPrice, distance);
                                }
                            }
                        }
                        else
                        {
                            double distance = position.EntryPrice - Symbol.Ask;

                            if (distance <= TrailingStopTrigger * Symbol.PipSize)
                            {
                                if (!isTrigerred)
                                {
                                    isTrigerred = true;
                                    Print("Trailing stop loss triggered on position {0}", position.Id);
                                }

                                double newStopLossPrice = Math.Round(Symbol.Ask + TrailingStopDistance * Symbol.PipSize, Symbol.Digits);

                                if (position.StopLoss == null || newStopLossPrice < position.StopLoss)
                                {
                                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                                    Print("Trigger at - {0}, New SL - {1}, distance - {2}", TrailingStopTrigger * Symbol.PipSize, newStopLossPrice, distance);
                                }
                            }
                        }
                    }


                }
            }
        }

        protected void Message(int level, string message)
        {
            Print("[{0}] {1}", level, message);
        }

        protected override void OnStop()
        {
            Print("Robot on hold");
        }
    }
}


J.
j.kozlowski.net

Joined on 15.11.2015

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: Daily Candle Breakout.algo
  • Rating: 0
  • Installs: 4856
Comments
Log in to add a comment.
FE
fernando-henrique · 3 years ago

Hello There!

How are you? What exacly this bot does?

Looking the code I perceved that it does not open position automatically.

Thank you very much.

 

TG
tgjobscv · 3 years ago

https://ctrader.com/forum/calgo-support/22008?page=1

up 07.2020

What are your ideas for improving the code after testing - for Sell trend in xauusd, xagusd, majors etc ?

SellSmartGrid - improving ?!

How 1. cTrader demo to live ctrader, mt4/mt5 Trade Copier ?

How 2. mt4/mt5 demo to live ctrader, mt4/mt5 Trade Copier ?

SK
Skypips · 5 years ago

Hi, it is great however would you be able to add Take Profit parameter in Pips?

MA
maffel · 7 years ago

please contact me for algo:  maffel@ymail.com

IR
ironmine · 8 years ago

Thank you for your cBot! Thanks for your efforts and sharing it.