NightBreakout cBot -- London breakout free

by guillermo in category Other at 15/09/2020
Description

I have programmed a very simple implementation of the London breakout strategy.

To know more about the London breakout strategy, see the videos in Youbute: https://www.youtube.com/results?search_query=london+breakout+strategy

This work has been inspired by this thread:
https://www.forexfactory.com/thread/730458-eurjpy-m15-system-80

However, the settings proposed by the author for EURJPY M15 in the first post are not profitable in my robot.

This robot has been tested for EURUSD, GBPJPY, USDJPY, EURJPY, CADJPY and CHFJPY, all of them in M15. Long term profitable configurations were found only for USDJPY and EURJPY.

If you find other profitable configurations for other symbols please let us know in the comments. If you know how to program and you manage to make this robot more profitable, let us know too.

Download the robot, the source code, the manual, settings files and more from this link:

https://drive.google.com/drive/folders/1O5qUPsnOd2THwuKmh4kWLS4Kl1W97aw6?usp=sharing

Before you ask questions about the robot, make sure that you have downloaded, read and understood the manual.


 

Warning! Executing the following cBot may result in loss of funds. Use it at your own risk.
Notification Publishing copyrighted material is strictly prohibited. If you believe there is copyrighted material in this section you may use the Copyright Infringement Notification form to submit a claim.
Formula / Source Code
Language: C#
Trading Platform: cAlgo
/*
Implementation of the London Breakout strategy
https://www.youtube.com/results?search_query=london+breakout+strategy

Thread in forum
https://www.forexfactory.com/thread/730458-eurjpy-m15-system-80

Expert advisor for MT4
https://www.forexfactory.com/thread/post/10715754#post10715754

Download manual, settings and more
https://drive.google.com/drive/folders/1O5qUPsnOd2THwuKmh4kWLS4Kl1W97aw6?usp=sharing
*/
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using System.Collections.Generic;


namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class NightBreakoutcBot : Robot
    {
        // ======================================================================

        [Parameter("GMT start hour", Group = "Box hours and size", DefaultValue = 22, MinValue = 0, MaxValue = 23)]
        public int ParamStartHour { get; set; }

        [Parameter("GMT start minute", Group = "Box hours and size", DefaultValue = 0, MinValue = 0, MaxValue = 59)]
        public int ParamStartMinute { get; set; }

        [Parameter("GMT end hour", Group = "Box hours and size", DefaultValue = 2, MinValue = 0, MaxValue = 23)]
        public int ParamEndHour { get; set; }

        [Parameter("GMT end minute", Group = "Box hours and size", DefaultValue = 0, MinValue = 0, MaxValue = 59)]
        public int ParamEndMinute { get; set; }

        [Parameter("Adjust summer time", Group = "Box hours and size", DefaultValue = true)]
        public bool ParamAdjustSummerTime { get; set; }

        [Parameter("Min box size (pips)", Group = "Box hours and size", DefaultValue = 15, MinValue = 0)]
        public int ParamMinBoxHeight { get; set; }

        [Parameter("Max box size (pips)", Group = "Box hours and size", DefaultValue = 45, MinValue = 0)]
        public int ParamMaxBoxHeight { get; set; }

        // ======================================================================

        // Choose fixed lot size or risk
        [Parameter("Method", Group = "Money management", DefaultValue = MMType.FIXED_LOT)]
        public MMType ParamMoneyMgmntType { get; set; }

        // Fixed lot size
        [Parameter("Lot size", Group = "Money management", DefaultValue = 0.01, MinValue = 0.01)]
        public double ParamLotSize { get; set; }

        // Percent of risk
        [Parameter("Risk %", Group = "Money management", DefaultValue = 2, MinValue = 0.1, MaxValue = 50.0)]
        public double ParamRisk { get; set; }

        // ===============================================================================

        [Parameter("Draw boxes in chart", Group = "Chart", DefaultValue = true)]
        public bool ParamDrawBoxes { get; set; }


        private string label = "NightBreakoutcBot";
        private string version = "1.0";

        private Status status;

        private double volume;

        private double hightakeProfitPrice;
        private double highStopLossPrice;
        private double highEntryPrice;
        private double lowtakeProfitPrice;
        private double lowStopLossPrice;
        private double lowEntryPrice;


        protected override void OnStart()
        {
            try
            {
                Positions.Closed += onPositionClosed;

                this.volume = Symbol.QuantityToVolumeInUnits(this.ParamLotSize);

                if (this.ParamDrawBoxes && (this.RunningMode == RunningMode.RealTime || this.RunningMode == RunningMode.VisualBacktesting))
                {
                    this.drawPastBoxes();
                }
                this.status = Status.IDLE;

                this.hightakeProfitPrice = 0.0;
                this.highStopLossPrice = 0.0;
                this.highEntryPrice = 0.0;
                this.lowtakeProfitPrice = 0.0;
                this.lowStopLossPrice = 0.0;
                this.lowEntryPrice = 0.0;

                if (this.RunningMode == RunningMode.RealTime || this.RunningMode == RunningMode.VisualBacktesting)
                {
                    Chart.DrawStaticText("version", "NightBreakout cBot " + this.version, VerticalAlignment.Bottom, HorizontalAlignment.Left, Chart.ColorSettings.ForegroundColor);
                }
                Print("Started " + this.label + " version " + this.version);

            } catch (Exception exc)
            {
                Print(exc.Message + " " + exc.StackTrace);
                throw exc;
            }
        }

        private void drawPastBoxes()
        {
            int finalIndex = Bars.Count - 1;
            for (int i = 30; i <= finalIndex; i++)
            {
                BoxHours boxHours = this.getBoxHours(Bars.OpenTimes[i]);
                int firstindex = 0;
                if (Bars.OpenTimes[i].Hour == boxHours.endHour && Bars.OpenTimes[i].Minute == this.ParamStartMinute)
                {
                    for (int j = i - 1; j > 0; j--)
                    {
                        if (Bars.OpenTimes[j].Hour == boxHours.startHour && Bars.OpenTimes[j].Minute == this.ParamEndMinute)
                        {
                            firstindex = j;
                            break;
                        }
                    }

                    double high = Bars.HighPrices[firstindex];
                    double low = Bars.LowPrices[firstindex];
                    for (int k = firstindex; k <= i; k++)
                    {
                        high = Math.Max(high, Bars.HighPrices[k]);
                        low = Math.Min(low, Bars.LowPrices[k]);
                    }
                    double delta = high - low;

                    Color color = Color.Lime;
                    if (delta < this.ParamMinBoxHeight * Symbol.PipSize || delta > this.ParamMaxBoxHeight * Symbol.PipSize)
                    {
                        color = Color.Red;
                    }

                    Chart.DrawRectangle(Guid.NewGuid().ToString(), firstindex, high, i, low, color, 2);
                    Chart.DrawTrendLine(Guid.NewGuid().ToString(), i, high + delta, i + 20, high + delta, color);
                    Chart.DrawTrendLine(Guid.NewGuid().ToString(), i, low - delta, i + 20, low - delta, color);
                }
            }
        }

        protected override void OnBar()
        {
            try
            {
                BoxHours boxHours = this.getBoxHours(Bars.OpenTimes.LastValue);
                if (this.status != Status.IDLE && Bars.OpenTimes.LastValue.Hour == boxHours.startHour && Bars.OpenTimes.LastValue.Minute == this.ParamStartMinute)
                {
                    this.status = Status.IDLE;
                }


                if (Bars.OpenTimes.LastValue.Hour == boxHours.endHour && Bars.OpenTimes.LastValue.Minute == this.ParamEndMinute)
                {
                    int lastIndex = Bars.OpenTimes.GetIndexByTime(Bars.OpenTimes.Last(1));
                    int firstIndex = 0;
                    for (int j = lastIndex - 1; j > 0; j--)
                    {
                        if (Bars.OpenTimes[j].Hour == boxHours.startHour && Bars.OpenTimes[j].Minute == this.ParamStartMinute)
                        {
                            firstIndex = j;
                            break;
                        }
                    }

                    double high = Bars.HighPrices[firstIndex];
                    double low = Bars.LowPrices[firstIndex];
                    for (int k = firstIndex; k <= lastIndex; k++)
                    {
                        high = Math.Max(high, Bars.HighPrices[k]);
                        low = Math.Min(low, Bars.LowPrices[k]);
                    }
                    double delta = high - low;

                    this.hightakeProfitPrice = high + delta;
                    this.highStopLossPrice = high - delta;
                    this.highEntryPrice = high;

                    this.lowtakeProfitPrice = low - delta;
                    this.lowStopLossPrice = low + delta;
                    this.lowEntryPrice = low;

                    this.status = Status.WAITING;

                    Color color = Color.Lime;
                    if (delta < this.ParamMinBoxHeight * Symbol.PipSize || delta > this.ParamMaxBoxHeight * Symbol.PipSize)
                    {
                        color = Color.Red;
                        this.status = Status.IDLE;
                    }
                    if (this.ParamDrawBoxes && (this.RunningMode == RunningMode.RealTime || this.RunningMode == RunningMode.VisualBacktesting))
                    {
                        Chart.DrawRectangle(Guid.NewGuid().ToString(), firstIndex, high, lastIndex, low, color, 2);
                        Chart.DrawTrendLine(Guid.NewGuid().ToString(), lastIndex, high + delta, lastIndex + 20, high + delta, color);
                        Chart.DrawTrendLine(Guid.NewGuid().ToString(), lastIndex, low - delta, lastIndex + 20, low - delta, color);
                    }
                }


                if (this.status == Status.WAITING)
                {
                    if (Bars.ClosePrices.LastValue > this.highEntryPrice && Bars.ClosePrices.Last(1) > this.highEntryPrice)
                    {
                        double deltaPips = (this.highEntryPrice - this.lowEntryPrice) / Symbol.PipSize;
                        TradeResult result = ExecuteMarketOrder(TradeType.Buy, Symbol.Name, this.calculateVolume(deltaPips), this.label, deltaPips, deltaPips);
                        if (result.IsSuccessful)
                        {
                            this.status = Status.TRADING;
                        }
                    }
                    if (Bars.ClosePrices.LastValue < this.lowEntryPrice && Bars.ClosePrices.Last(1) < this.lowEntryPrice)
                    {
                        double deltaPips = (this.highEntryPrice - this.lowEntryPrice) / Symbol.PipSize;
                        TradeResult result = ExecuteMarketOrder(TradeType.Sell, Symbol.Name, this.calculateVolume(deltaPips), this.label, deltaPips, deltaPips);
                        if (result.IsSuccessful)
                        {
                            this.status = Status.TRADING;
                        }
                    }
                }

            } catch (Exception exc)
            {
                Print(exc.Message + " " + exc.StackTrace);
                throw exc;
            }
        }

        private void onPositionClosed(PositionClosedEventArgs args)
        {
            if (args.Position.SymbolName.Equals(Symbol.Name) && this.label.Equals(args.Position.Label))
            {
                this.status = Status.IDLE;
            }
        }

        private BoxHours getBoxHours(DateTime dt)
        {
            BoxHours bh = new BoxHours();
            if (this.ParamAdjustSummerTime)
            {
                if (this.isSummer(dt))
                {
                    bh.startHour = this.ParamStartHour - 1;
                    if (bh.startHour < 0)
                    {
                        bh.startHour += 24;
                    }
                    bh.endHour = this.ParamEndHour - 1;
                    if (bh.endHour < 0)
                    {
                        bh.endHour += 24;
                    }
                }
                else
                {
                    bh.startHour = this.ParamStartHour;
                    bh.endHour = this.ParamEndHour;
                }
            }
            else
            {
                bh.startHour = this.ParamStartHour;
                bh.endHour = this.ParamEndHour;
            }
            return bh;
        }

        private bool isSummer(DateTime dt)
        {
            // Winter: November to March
            // Summer: April to October
            return dt.Month >= 4 && dt.Month <= 10;
        }

        private double calculateVolume(double stopLossPips)
        {
            double vol = this.volume;
            switch (this.ParamMoneyMgmntType)
            {
                case MMType.FIXED_LOT:
                    vol = this.volume;
                    break;

                case MMType.RISK:
                    double conversionRate = 1.0;
                    if (Account.Currency.Equals("EUR"))
                    {
                        Symbol symbolEURYYY = Symbols.GetSymbol("EUR" + Symbol.Name.Substring(3, 3));
                        conversionRate = symbolEURYYY.Bid;
                    }
                    vol = this.normalizeVolume(Account.Equity * this.ParamRisk * conversionRate / (100.0 * stopLossPips * Symbol.PipSize));
                    break;
            }
            return vol;
        }

        private double normalizeVolume(double vol)
        {
            double result = 1000;
            while (result + 1000 < vol)
            {
                result += 1000;
            }
            return result;
        }
    }

    public class BoxHours
    {
        public int startHour { get; set; }
        public int endHour { get; set; }
    }

    public enum Status
    {
        IDLE,
        WAITING,
        TRADING
    }

    public enum MMType
    {
        FIXED_LOT,
        RISK
    }
}
Comments
0