Grid Not working properly, please help

Created at 27 Apr 2020, 05:02
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!
MR

Mr4x

Joined 12.04.2019

Grid Not working properly, please help
27 Apr 2020, 05:02


Hi,

I have created a cBot that follows a moving average. Basically whenever candles are below the moving average, only sell trades are placed, and vice versa above the moving average.

This cBot is supposed to grid in the opposite direction if the moving average is crossed and there are trades still active trades from the other side. For example:

  • There are 3 Sell trades open, each spaced 10 pips apart as per grid rules as price rises
  • Price moves above moving average, so now I want to cease placing Sell trades and only place Buy trades as the price goes up every 10 pips until the grid is in profit
  • Entire grid is then closed out for profit and restarted as per moving average rule

I have created a cBot that seems to grid perfectly on each side of the moving average when there are only trades open in the right direction (ie: the grid places sell trades as price move up every 10 pips below the moving average, and places buy trades as price moves down every 10 pips above moving average).

Bot seems to come unstuck a little when there are open trades on one side of the moving average and then crosses over. Instead of only placing trades every 10 pips up or down (aka the "AntiGrid" in my code), the bot seems to place a whole heap of consecutive trades all within 5 pips of each other, and I cannot figure out why. If anybody could run a quick eye over the code (specifically the antigrid and the way it is invoked) and suggest why it may not be working, that would be great.

Thank you.

//////////////////////////////////////////////////////////

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.UTC, AccessRights = AccessRights.None)]
    public class SmartGridV3 : Robot
    {
        [Parameter("Maximum open buy position?", Group = "Basic Setup", DefaultValue = 20, MinValue = 0)]
        public int MaxOpenBuy { get; set; }

        [Parameter("Maximum open Sell position?", Group = "Basic Setup", DefaultValue = 20, MinValue = 0)]
        public int MaxOpenSell { get; set; }

        [Parameter("Pip step", Group = "Basic Setup", DefaultValue = 10, MinValue = 1)]
        public int PipStep { get; set; }

        [Parameter("Stop loss pips each position", Group = "Basic Setup", DefaultValue = 100, MinValue = 10, Step = 10)]
        public double StopLossPips { get; set; }

        [Parameter("Stop loss $$ Handbrake", Group = "Basic Setup", DefaultValue = 100, MinValue = 10, Step = 10)]
        public double StopLossHB { get; set; }

        [Parameter("First order volume", Group = "Basic Setup", DefaultValue = 1000, MinValue = 1, Step = 1)]
        public double FirstVolume { get; set; }

        [Parameter("Max spread allowed to open position", Group = "Basic Setup", DefaultValue = 3.0)]
        public double MaxSpread { get; set; }

        [Parameter("Target profit for each group of trade", Group = "Basic Setup", DefaultValue = 3, MinValue = 1)]
        public int AverageTakeProfit { get; set; }

        [Parameter("Debug flag, set to No on real account to avoid closing all positions when stoping this cBot", Group = "Advanced Setup", DefaultValue = false)]
        public bool IfCloseAllPositionsOnStop { get; set; }

        [Parameter("MA Type", Group = "Moving Average", DefaultValue = MovingAverageType.Exponential)]
        public MovingAverageType MAType { get; set; }

        [Parameter("MA Period", Group = "Moving Average", DefaultValue = 14)]
        public int MAPeriod { get; set; }

        [Parameter("Signal candle,in bars", Group = "Moving Average", DefaultValue = 1, MinValue = 0)]
        public int SignalCandle { get; set; }

        [Parameter("Volume exponent", Group = "Advanced Setup", DefaultValue = 1.0, MinValue = 0.1, MaxValue = 5.0)]
        public double VolumeExponent { get; set; }

        private string ThiscBotLabel;
        public double balance;
        private MovingAverage MA;
        private DateTime LastBuyTradeTime;
        private DateTime LastSellTradeTime;

        // cBot initialization
        protected override void OnStart()
        {

            balance = Account.Balance;
            Print(balance);
            // Set position label to cBot name
            ThiscBotLabel = this.GetType().Name;
            // Normalize volume in case a wrong volume was entered
            if (FirstVolume != (FirstVolume = Symbol.NormalizeVolumeInUnits(FirstVolume)))
            {
                Print("Volume entered incorrectly, volume has been changed to ", FirstVolume);
            }

            MA = Indicators.MovingAverage(MarketSeries.Close, MAPeriod, MAType);
        }

        // Error handling
        protected override void OnError(Error error)
        {
            Print("Error occured, error code: ", error.Code);
        }

        protected override void OnTick()
        {

            // Close all positions if all buy positions' target profit is met
            if (Positions.Count(x => x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                if (Positions.Where(x => x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Average(x => x.NetProfit) >= FirstVolume * AverageTakeProfit * Symbol.PipSize)
                {
                    foreach (var position in Positions)
                    {
                        if (position.SymbolName == SymbolName && position.Label == ThiscBotLabel)
                            ClosePosition(position);

                        balance = Account.Balance;
                        Print(balance);
                    }
                }
            }

            {
                if (Account.Equity <= balance - StopLossHB)
                {

                    foreach (var position in Positions)
                    {
                        ClosePosition(position);
                    }
                    balance = Account.Balance;
                    Print(balance);
                }
            }

            // Conditions check before process trade
            if (Symbol.Spread / Symbol.PipSize <= MaxSpread)
            {
                if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) < MaxOpenBuy)
                    if (MA.Result.Last(SignalCandle) < MarketSeries.Close.Last(SignalCandle))
                        if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) == 0)
                            ProcessBuy();
                        else if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
                            ProcessAntiBuy();

                if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) < MaxOpenSell)
                    if (MA.Result.Last(SignalCandle) > MarketSeries.Close.Last(SignalCandle))
                        if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) == 0)
                            ProcessSell();
                        else if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
                            ProcessAntiSell();

            }

            if (!this.IsBacktesting)
                DisplayStatusOnChart();
        }

        protected override void OnStop()
        {
            Chart.RemoveAllObjects();
            // Close all open positions opened by this cBot on stop
            if (this.IsBacktesting || IfCloseAllPositionsOnStop)
            {
                foreach (var position in Positions)
                {
                    if (position.SymbolName == SymbolName && position.Label == ThiscBotLabel)
                        ClosePosition(position);
                }
            }
        }
//Grid when above moving average
        private void ProcessBuy()
        {

            if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) == 0)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, FirstVolume, ThiscBotLabel, StopLossPips, null);
                LastBuyTradeTime = MarketSeries.OpenTime.Last(0);
            }

            if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                if (Symbol.Ask < (Positions.Where(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Min(x => x.EntryPrice) - PipStep * Symbol.PipSize) && LastBuyTradeTime != MarketSeries.OpenTime.Last(0))
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, CalculateVolume(TradeType.Buy), ThiscBotLabel, StopLossPips, null);
                    LastBuyTradeTime = MarketSeries.OpenTime.Last(0);
                }
            }
        }
//Grid when below moving average
        private void ProcessSell()
        {

            if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) == 0)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, FirstVolume, ThiscBotLabel, StopLossPips, null);
                LastSellTradeTime = MarketSeries.OpenTime.Last(1);
            }

            if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                if (Symbol.Bid > (Positions.Where(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Max(x => x.EntryPrice) + PipStep * Symbol.PipSize) && LastSellTradeTime != MarketSeries.OpenTime.Last(1))
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, CalculateVolume(TradeType.Sell), ThiscBotLabel, StopLossPips, null);
                    LastSellTradeTime = MarketSeries.OpenTime.Last(1);
                }
            }
        }
//Grid when above moving average but positions still open below moving average
        private void ProcessAntiBuy()
        {

            if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) == 0)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, FirstVolume, ThiscBotLabel, StopLossPips, null);
                LastBuyTradeTime = MarketSeries.OpenTime.Last(0);
            }

            if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                if (Symbol.Ask > (Positions.Where(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Min(x => x.EntryPrice) + PipStep * Symbol.PipSize) && LastBuyTradeTime != MarketSeries.OpenTime.Last(0))
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, CalculateVolume(TradeType.Buy), ThiscBotLabel, StopLossPips, null);
                    LastBuyTradeTime = MarketSeries.OpenTime.Last(0);
                }
            }
        }
//Grid when below moving average but positions still open above moving average
        private void ProcessAntiSell()
        {

            if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) == 0)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, FirstVolume, ThiscBotLabel, StopLossPips, null);
                LastSellTradeTime = MarketSeries.OpenTime.Last(1);
            }

            if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                if (Symbol.Bid < (Positions.Where(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Max(x => x.EntryPrice) - PipStep * Symbol.PipSize) && LastSellTradeTime != MarketSeries.OpenTime.Last(1))
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, CalculateVolume(TradeType.Sell), ThiscBotLabel, StopLossPips, null);
                    LastSellTradeTime = MarketSeries.OpenTime.Last(1);
                }
            }
        }

        private double CalculateVolume(TradeType tradeType)
        {
            return Symbol.NormalizeVolumeInUnits(FirstVolume * Math.Pow(VolumeExponent, Positions.Count(x => x.TradeType == tradeType && x.SymbolName == SymbolName && x.Label == ThiscBotLabel)));
        }

        private void DisplayStatusOnChart()
        {
            if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 1)
            {
                var y = Positions.Where(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Average(x => x.EntryPrice);
                Chart.DrawHorizontalLine("bpoint", y, Color.Yellow, 2, LineStyle.Dots);
            }
            else
                Chart.RemoveObject("bpoint");
            if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 1)
            {
                var z = Positions.Where(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Average(x => x.EntryPrice);
                Chart.DrawHorizontalLine("spoint", z, Color.HotPink, 2, LineStyle.Dots);
            }
            else
                Chart.RemoveObject("spoint");
            Chart.DrawStaticText("pan", GenerateStatusText(), VerticalAlignment.Top, HorizontalAlignment.Left, Color.Tomato);
        }

        private string GenerateStatusText()
        {
            var statusText = "";
            var buyPositions = "";
            var sellPositions = "";
            var spread = "";
            var buyDistance = "";
            var sellDistance = "";
            spread = "\nSpread = " + Math.Round(Symbol.Spread / Symbol.PipSize, 1);
            buyPositions = "\nBuy Positions = " + Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel);
            sellPositions = "\nSell Positions = " + Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel);
            if (Positions.Count(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                var averageBuyFromCurrent = Math.Round((Positions.Where(x => x.TradeType == TradeType.Buy && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Average(x => x.EntryPrice) - Symbol.Bid) / Symbol.PipSize, 1);
                buyDistance = "\nBuy Target Away = " + averageBuyFromCurrent;
            }
            if (Positions.Count(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel) > 0)
            {
                var averageSellFromCurrent = Math.Round((Symbol.Ask - Positions.Where(x => x.TradeType == TradeType.Sell && x.SymbolName == SymbolName && x.Label == ThiscBotLabel).Average(x => x.EntryPrice)) / Symbol.PipSize, 1);
                sellDistance = "\nSell Target Away = " + averageSellFromCurrent;
            }
            if (Symbol.Spread / Symbol.PipSize > MaxSpread)
                statusText = "MAX SPREAD EXCEED";
            else
                statusText = ThiscBotLabel + buyPositions + spread + sellPositions + buyDistance + sellDistance;
            return (statusText);
        }
    }
}


@Mr4x