Category Trend  Published on 30/07/2018

Lines Trader

Description

The below cBot has been developed to demonstrate some of the new features of cTrader Automate API 3.01. The "LinesTrader cBot" allows traders to draw lines on chart which can be used a break out or retracement lines. If the price crosses a Breakout Line then an order towards the crossing direction is placed. If the price crosses a Retracement Line then an order towards the opposite direction of the crossing is placed.

Note: The cBot currently works only on Spotware cTrader Public Beta which has been upgraded to version 3.01.


// -------------------------------------------------------------------------------------------------
//
//    This code is a cAlgo API sample.
//
//    This cBot is intended to be used as a sample and does not guarantee any particular outcome or
//    profit of any kind. Use it at your own risk
//
//    The "LinesTrader cBot" allows traders to draw lines on chart which can be used a break out or retracement lines. 
//    If the price crosses a Breakout Line then an order towards the crossing direction is placed.
//    If the price crosses a Retracement Line then an order towards the opposite direction of the crossing is placed
//
// -------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using cAlgo.API;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class LinesTrader : Robot
    {
        private const string HowToUseText = "How to use:\nCtrl + Left Mouse Button - Draw Breakout line\nShift + Left Mouse Button - Draw Retracement line";

        private const string HowToUseObjectName = "LinesTraderText";

        [Parameter("Stop Loss Pips", DefaultValue = 0.0, MinValue = 0.0, Step = 1)]
        public double StopLossPips { get; set; }

        [Parameter("Take Profit Pips", DefaultValue = 0.0, MinValue = 0.0, Step = 1)]
        public double TakeProfitPips { get; set; }

        [Parameter("Volume", DefaultValue = 1000, MinValue = 0, Step = 1)]
        public double Volume { get; set; }

        [Parameter("Trade", DefaultValue = true)]
        public bool IsTradingAllowed { get; set; }

        [Parameter("Send Email", DefaultValue = false)]
        public bool IsEmailAllowed { get; set; }

        [Parameter("Email address")]
        public string EmailAddress { get; set; }

        [Parameter("Show How To Use", DefaultValue = true)]
        public bool ShowHowToUse { get; set; }

        private SignalLineDrawManager DrawManager { get; set; }
        private SignalLineRepository SignalLineRepository { get; set; }

        protected override void OnStart()
        {
            DrawManager = new SignalLineDrawManager(Chart);
            SignalLineRepository = new SignalLineRepository(Chart);

            if (ShowHowToUse)
            {
                ShowHowToUseText();
                Chart.MouseDown += OnChartMouseDown;
            }
        }

        protected override void OnTick()
        {
            var lastBarIndex = MarketSeries.Close.Count - 1;
            foreach (var signalLine in SignalLineRepository.GetLines())
                if (signalLine.CanExecute(Symbol.Bid, lastBarIndex))
                {
                    signalLine.MarkAsExecuted();
                    var message = string.Format("{0} Signal line {1}{2} was executed on {3}", Time, signalLine.TradeType, signalLine.SignalType, Symbol.Code);
                    Print(message);

                    if (IsTradingAllowed)
                    {
                        ExecuteMarketOrder(signalLine.TradeType, Symbol, Volume, "LinesTrader cBot", StopLossPips, TakeProfitPips);
                    }
                    if (IsEmailAllowed)
                    {
                        Notifications.SendEmail(EmailAddress, EmailAddress, "LinesTrader Alert", message);
                    }
                }
        }

        protected override void OnStop()
        {
            SignalLineRepository.Dispose();
            DrawManager.Dispose();
        }

        private void OnChartMouseDown(ChartMouseEventArgs args)
        {
            Chart.MouseDown -= OnChartMouseDown;
            HideHowToUseText();
        }

        private void ShowHowToUseText()
        {
            Chart.DrawStaticText(HowToUseObjectName, HowToUseText, VerticalAlignment.Center, HorizontalAlignment.Center, Chart.ColorSettings.ForegroundColor);
        }

        private void HideHowToUseText()
        {
            Chart.RemoveObject(HowToUseObjectName);
        }
    }

    public class SignalLineDrawManager : IDisposable
    {
        private readonly Chart _chart;
        private ProtoSignalLine _currentProtoSignalLine;
        private const string StatusTextName = "ProtoSignalLineStatus";



        public SignalLineDrawManager(Chart chart)
        {
            _chart = chart;
            _chart.DragStart += OnChartDragStart;
            _chart.DragEnd += OnChartDragEnd;
            _chart.Drag += OnChartDrag;
        }

        private void OnChartDragStart(ChartDragEventArgs args)
        {
            if (args.ChartArea != args.Chart)
                return;

            var signalType = GetSignalType(args);
            if (signalType.HasValue)
            {
                _chart.IsScrollingEnabled = false;
                _currentProtoSignalLine = new ProtoSignalLine(_chart, signalType, args.TimeValue, args.YValue);
                UpdateStatus();
            }
            else
            {
                _currentProtoSignalLine = null;
            }
        }

        private void OnChartDragEnd(ChartDragEventArgs args)
        {
            if (_currentProtoSignalLine != null)
            {
                _currentProtoSignalLine.Complete(args.TimeValue, args.YValue);
                _currentProtoSignalLine = null;

                _chart.IsScrollingEnabled = true;
                _chart.RemoveObject(StatusTextName);
            }
        }

        private void OnChartDrag(ChartDragEventArgs args)
        {
            if (_currentProtoSignalLine != null)
            {
                _currentProtoSignalLine.Update(args.TimeValue, args.YValue);
                UpdateStatus();
            }
        }

        private void UpdateStatus()
        {
            var text = string.Format("Creating {0} line", _currentProtoSignalLine.LineLabel);
            _chart.DrawStaticText(StatusTextName, text, VerticalAlignment.Top, HorizontalAlignment.Left, _chart.ColorSettings.ForegroundColor);
        }

        private SignalType? GetSignalType(ChartDragEventArgs args)
        {
            if (args.CtrlKey && !args.ShiftKey)
                return SignalType.Breakout;
            if (!args.CtrlKey && args.ShiftKey)
                return SignalType.Retracement;

            return null;
        }

        public void Dispose()
        {
            _chart.DragStart -= OnChartDragStart;
            _chart.DragEnd -= OnChartDragEnd;
            _chart.Drag -= OnChartDrag;
        }
    }

    public class SignalLineRepository : IDisposable
    {
        private readonly Chart _chart;
        private readonly Dictionary<string, SignalLine> _signalLines;

        public SignalLineRepository(Chart chart)
        {
            _chart = chart;
            _signalLines = new Dictionary<string, SignalLine>();

            foreach (var chartTrendLine in chart.FindAllObjects<ChartTrendLine>())
                TryAddSignalLine(chartTrendLine);

            _chart.ObjectAdded += OnChartObjectAdded;
            _chart.ObjectRemoved += OnChartObjectRemoved;
            _chart.ObjectUpdated += OnChartObjectUpdated;
        }

        public IEnumerable<SignalLine> GetLines()
        {
            return _signalLines.Values;
        }

        private void TryAddSignalLine(ChartObject chartObject)
        {
            var chartTrendLine = chartObject as ChartTrendLine;
            if (chartTrendLine != null && IsSignalLine(chartTrendLine))
                _signalLines.Add(chartTrendLine.Name, CreateSignalLine(chartTrendLine));
        }

        private void TryRemoveSignalLine(ChartObject chartObject)
        {
            if (_signalLines.ContainsKey(chartObject.Name))
                _signalLines.Remove(chartObject.Name);
        }

        private void OnChartObjectAdded(ChartObjectAddedEventArgs args)
        {
            if (args.Area != args.Chart)
                return;

            TryAddSignalLine(args.ChartObject);
        }

        private void OnChartObjectUpdated(ChartObjectUpdatedEventArgs args)
        {
            if (args.Area != args.Chart)
                return;

            TryRemoveSignalLine(args.ChartObject);
            TryAddSignalLine(args.ChartObject);
        }

        private void OnChartObjectRemoved(ChartObjectRemovedEventArgs args)
        {
            if (args.Area != args.Chart)
                return;

            TryRemoveSignalLine(args.ChartObject);
        }

        private SignalLine CreateSignalLine(ChartTrendLine chartTrendLine)
        {
            var signalType = GetLineSignalType(chartTrendLine);
            var tradeType = GetLineTradeType(chartTrendLine);
            var signalLine = new SignalLine(chartTrendLine, signalType, tradeType);
            return signalLine;
        }

        private bool IsSignalLine(ChartTrendLine line)
        {
            return SignalLineLabels.AllLables.Contains(line.Comment);
        }

        private SignalType GetLineSignalType(ChartTrendLine line)
        {
            var comment = line.Comment;
            if (comment == SignalLineLabels.BuyBreakoutLabel || comment == SignalLineLabels.SellBreakoutLabel)
                return SignalType.Breakout;
            if (comment == SignalLineLabels.BuyRetraceLabel || comment == SignalLineLabels.SellRetraceLabel)
                return SignalType.Retracement;
            throw new ArgumentException();
        }

        private TradeType GetLineTradeType(ChartTrendLine line)
        {
            var comment = line.Comment;
            if (comment == SignalLineLabels.BuyBreakoutLabel || comment == SignalLineLabels.BuyRetraceLabel)
                return TradeType.Buy;
            if (comment == SignalLineLabels.SellBreakoutLabel || comment == SignalLineLabels.SellRetraceLabel)
                return TradeType.Sell;
            throw new ArgumentException();
        }

        public void Dispose()
        {
            _chart.ObjectAdded -= OnChartObjectAdded;
            _chart.ObjectRemoved -= OnChartObjectRemoved;
            _chart.ObjectUpdated -= OnChartObjectUpdated;
        }
    }

    public class ProtoSignalLine
    {

        private static readonly Color BuyLineColor = Color.Green;
        private static readonly Color SellLineColor = Color.Red;

        private readonly Chart _chart;
        private readonly ChartTrendLine _line;
        private readonly SignalType? _signalType;

        public ProtoSignalLine(Chart chart, SignalType? signalType, DateTime startTimeValue, double startYValue)
        {
            _chart = chart;
            _signalType = signalType;

            _line = _chart.DrawTrendLine(string.Format("LinesTrader {0:N}", Guid.NewGuid()), startTimeValue, startYValue, startTimeValue, startYValue, LineColor);

            _line.ExtendToInfinity = true;
            _line.Thickness = 2;
            _line.IsInteractive = true;
        }

        private bool IsPriceAboveLine
        {
            get { return _line != null && _chart.Symbol.Bid >= _line.CalculateY(_chart.MarketSeries.Close.Count - 1); }
        }

        private TradeType LineTradeType
        {
            get { return _signalType == SignalType.Breakout ? (IsPriceAboveLine ? TradeType.Sell : TradeType.Buy) : (IsPriceAboveLine ? TradeType.Buy : TradeType.Sell); }
        }

        private Color LineColor
        {
            get { return LineTradeType == TradeType.Buy ? BuyLineColor : SellLineColor; }
        }

        public string LineLabel
        {
            get { return _signalType == SignalType.Breakout ? (LineTradeType == TradeType.Buy ? SignalLineLabels.BuyBreakoutLabel : SignalLineLabels.SellBreakoutLabel) : (LineTradeType == TradeType.Buy ? SignalLineLabels.BuyRetraceLabel : SignalLineLabels.SellRetraceLabel); }
        }

        private bool CanComplete
        {
            get { return _line.Time1 != _line.Time2 || Math.Abs(_line.Y1 - _line.Y2) >= _chart.Symbol.PipValue; }
        }

        public void Update(DateTime timeValue, double yValue)
        {
            _line.Time2 = timeValue;
            _line.Y2 = yValue;
            _line.Color = LineColor;
        }

        public void Complete(DateTime timeValue, double yValue)
        {
            Update(timeValue, yValue);

            if (CanComplete)
            {
                _line.Comment = LineLabel;
                _line.IsInteractive = true;
            }
            else
            {
                _chart.RemoveObject(_line.Name);
            }
        }
    }

    public class SignalLine
    {
        public TradeType TradeType { get; private set; }
        public SignalType SignalType { get; private set; }

        private readonly ChartTrendLine _chartTrendLine;



        public SignalLine(ChartTrendLine chartTrendLine, SignalType signalType, TradeType tradeType)
        {
            _chartTrendLine = chartTrendLine;
            SignalType = signalType;
            TradeType = tradeType;
        }

        public void MarkAsExecuted()
        {
            _chartTrendLine.Thickness = 1;
            _chartTrendLine.Color = Color.FromArgb(150, _chartTrendLine.Color);
        }

        public bool CanExecute(double price, int barIndex)
        {
            if (_chartTrendLine.Thickness <= 1)
                return false;

            var lineValue = _chartTrendLine.CalculateY(barIndex);

            switch (SignalType)
            {
                case SignalType.Breakout:
                    return CanExecuteForBreakout(price, lineValue);
                case SignalType.Retracement:
                    return CanExecuteForRetrace(price, lineValue);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private bool CanExecuteForBreakout(double price, double lineValue)
        {
            return TradeType == TradeType.Buy && price > lineValue || TradeType == TradeType.Sell && price < lineValue;
        }

        private bool CanExecuteForRetrace(double price, double lineValue)
        {
            return TradeType == TradeType.Buy && price <= lineValue || TradeType == TradeType.Sell && price >= lineValue;
        }
    }

    public enum SignalType
    {
        Breakout,
        Retracement
    }

    public static class SignalLineLabels
    {
        public const string BuyBreakoutLabel = "BuyBreakout";
        public const string SellBreakoutLabel = "SellBreakout";
        public const string BuyRetraceLabel = "BuyRetracement";
        public const string SellRetraceLabel = "SellRetracement";

        public static readonly string[] AllLables = 
        {
            BuyBreakoutLabel,
            SellBreakoutLabel,
            BuyRetraceLabel,
            SellRetraceLabel
        };
    }
}


Spotware's avatar
Spotware

Joined on 23.09.2013

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: LinesTrader.algo
  • Rating: 0
  • Installs: 3498
  • Modified: 13/10/2021 09:55
Comments
Log in to add a comment.
CA
caputojr · 4 months ago

Do you know any bot like this but instead of placing the lines at the price chart you can also place lines in the indicator chart so it will place order based of the indicator line reached?

 

Thanks

KA
kaliuta3 · 4 years ago

THANKS. WORKING.

Nogivenup's avatar
Nogivenup · 5 years ago

hello i have an issue to install my cbots can you help please?
it says: 
''can not Install ....1.0algo''
Unable to load assembly: Unsupported parameter type 'DayOfWeek'

I am using Ctrader desktop from Icmarket 3.3 version

HI
hiba7rain · 6 years ago

Great thing, most of ctrader users waiting for it long time 

so when the new update will be relesed 

WH
whoknows19500 · 6 years ago

reminds me of TrendMeLeaveMe.ex4 from MT4. Great tool. When can we expect to see this on Public Brokerages. How soon will 3.0.1 be avaiable? 

 

Great work Spotware.

PanagiotisCharalampous's avatar
PanagiotisCharalampous · 6 years ago

Hi all,

Please note that at the moment this is only working for Spotware Public Beta 3.01. This version has not beed rolled out to brokers yet.

DA
Danis · 6 years ago

How is possible to publish something that was not testing for building errors before?

DA
Danis · 6 years ago

Not working.

Tray to fix this kind of errors:

Error CS0246: The type or namespace name 'ChartMouseEventArgs' could not be found (are you missing a using directive or an assembly reference?)

Error CS0246: The type or namespace name 'ChartObject' could not be found (are you missing a using directive or an assembly reference?)

Error CS0246: The type or namespace name 'ChartObjectAddedEventArgs' could not be found (are you missing a using directive or an assembly reference?)

IA
IandelMar · 6 years ago

Dont work

F.
f.finke@arcor.de · 6 years ago

Hello, th eupdate with backtesting is very nich. But what are the settings. when i make both 5 Pips dont work?

What is when the bot dont work like the backtesting?

The visual Mode is very nice :-)