Replies

duketv
16 Apr 2021, 13:02

RE:

amusleh said:

Hi,

The methods works the way I explained on my previous post, just tested with this sample:

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

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class HasCrossedSample : Indicator
    {
        private SimpleMovingAverage _fastMa, _slowMa;

        [Parameter("Type", DefaultValue = CrossType.Above)]
        public CrossType CrossType { get; set; }
        
        [Parameter("Period", DefaultValue = 10)]
        public int Period { get; set; }

        [Output("Fast MA", LineColor = "Red", Thickness = 1)]
        public IndicatorDataSeries FastMa { get; set; }

        [Output("Slow MA", LineColor = "Yellow", Thickness = 2)]
        public IndicatorDataSeries SlowMa { get; set; }

        protected override void Initialize()
        {
            _fastMa = Indicators.SimpleMovingAverage(Bars.ClosePrices, 9);
            _slowMa = Indicators.SimpleMovingAverage(Bars.ClosePrices, 20);
        }

        public override void Calculate(int index)
        {
            FastMa[index] = _fastMa.Result[index];
            SlowMa[index] = _slowMa.Result[index];

            if (!IsLastBar) return;

            Chart.DrawVerticalLine("line", index - Period, Color.Red, 2);

            if (CrossType == CrossType.Above && FastMa.HasCrossedAbove(SlowMa, Period)) Print("Crossed");
            else if (CrossType == CrossType.Below && FastMa.HasCrossedBelow(SlowMa, Period)) Print("Crossed");
        }
    }
    
    public enum CrossType
    {
        Above,
        Below
    }
}

The sample indicator will draw a vertical line on period start, and then if fast MA crossed above/below slow MA it will print "Crossed" on log.

A cross must happen during the period values.

Hei amusleh,

Thanks a bunch for that code! That solved the problem and showed me that you were 100% right. Hascrossedabove works as advertised. I did found out that in my code tthat the Symbol.Bid caused all the problems. I swapped it to Bars.Openprices and the problem is gone. Thanks for the support and effort!

ar SmaBuy = _sma1.Result.HasCrossedBelow(Symbol.Bid, 3);

@duketv

duketv
06 Apr 2021, 20:17

RE:

amusleh said:

Hi,

The methods check if there was any cross in x last periods, if there was any it returns true otherwise it returns false.

If you use HasCrossedAbove with 10 as period, then it checks if is the crossing series has crossed at least one time the crossed series upward in last 10 values or not, if it did then the method returns true otherwise it returns false.

Same for HasCrossedBelow, except it checks for downward cross.

An up cross happens if a value inside crossing series goes above//equal to crossed series value and previous value of crossing series was below crossed series value.

A down cross happens if a value inside crossing series goes below//equal to crossed series value and previous value of crossing series was above crossed series value.

 

Thanks! That is exactly how I understand it as well but that is not what I see happening.
Because if your period is set to 10 than you would see the method return true for 10 bars (if set to on bar), but instead I see an irregular amount of true returned.

In the below bot I have set it to print a message to the log, everytime the method returns true. Am i missing something or is there perhaps a bug?
 

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 NewcBot : Robot
    {

        [Parameter("Source SMA #1")]
        public DataSeries SourceSma1 { get; set; }

        [Parameter("Period SMA #1", DefaultValue = 5, MinValue = 1, MaxValue = 100)]
        public int PeriodsSma1 { get; set; }



        private SimpleMovingAverage _sma1 { get; set; }




        protected override void OnStart()
        {
            _sma1 = Indicators.SimpleMovingAverage(SourceSma1, PeriodsSma1);
        }

        protected override void OnTick()
        {
            // Put your core logic here
        }


        protected override void OnBar()
        {

            var SmaBuy = _sma1.Result.HasCrossedBelow(Symbol.Bid, 3);
            var SmaSell = _sma1.Result.HasCrossedAbove(Symbol.Bid, 3);

            if (SmaBuy == true)
            {
                Print("SmaBuy = True");
            }

            if (SmaSell == true)
            {
                Print("SmaSell = True");
            }


        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
    }
}

 


@duketv

duketv
05 Apr 2021, 18:45

RE:

PanagiotisCharalampous said:

Hi duketv,

I think the documentation is clear. If you think there is a bug, please provide us some examples to reproduce and investigate.

Best Regards,

Panagiotis 

Join us on Telegram

Dear PanagiotisCharalampous ,

Thank you for taking the time to reply to this. In the below code I have added a 'print message in the log' after every cross over between the price and a simple SMA. I have set the period to 3, so I expect the method will return 3x 'true', but it doesn't. Checked on the BTC/EUR pair with actual tick data.
Can you explain this irregular amount of 'true' ?

Many thanks

 

 

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 NewcBot : Robot
    {

        [Parameter("Source SMA #1")]
        public DataSeries SourceSma1 { get; set; }

        [Parameter("Period SMA #1", DefaultValue = 5, MinValue = 1, MaxValue = 100)]
        public int PeriodsSma1 { get; set; }



        private SimpleMovingAverage _sma1 { get; set; }




        protected override void OnStart()
        {
            _sma1 = Indicators.SimpleMovingAverage(SourceSma1, PeriodsSma1);
        }

        protected override void OnTick()
        {
            // Put your core logic here
        }


        protected override void OnBar()
        {

            var SmaBuy = _sma1.Result.HasCrossedBelow(Symbol.Bid, 3);
            var SmaSell = _sma1.Result.HasCrossedAbove(Symbol.Bid, 3);

            if (SmaBuy == true)
            {
                Print("SmaBuy = True");
            }

            if (SmaSell == true)
            {
                Print("SmaSell = True");
            }


        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
    }
}

 


@duketv

duketv
01 Apr 2021, 14:58

hi guys,
I am still hoping someone can explain how this 'hascrossedover' function exactly works with respect to the period, because I see a variable number of 'true' returns with respect to this period
The documentation says: "HasCrossedAbove will compare the crossing dataseries to the crossed dataseries starting from the current value of the series going back the specified period"

Thank you.


@duketv

duketv
21 Mar 2021, 16:27

RE: RE: RE: RE:

Thank you, I have read the documentation before of this method but still unclear exactly how it works. My interpretation of 'hascrossedabove' is that after crossing, the method will return true for an x amount of bars. X being the 'int Period' from the method. However, I do see an irregular amount 'true' when I code for this.

Any exact explanation is appreciated. thanks

 

amusleh said:

 

You can read the documentation of HasCroossedAbove method here: 

 


@duketv

duketv
19 Mar 2021, 15:38

RE: RE:

amusleh said:

Hi,

The code above checks last MA value which is not closed yet and can change, so it can go above or below last closed bar price.

You should use Last(1) not LastValue:

if (Bars.Last(1).Open < Sma.Result.Last(1) && Bars.Last(1).Close > Sma.Result.Last(1))
{

​}

Last(1) gives you the latest closed value, and LastValue gives you the last value of series which is not closed yet and can change which will give you inconsistent results both in backtest and live.

You can use Last(2) to get the last second closed value of a series.

Thank you, That is indeed some valuable information for further coding.. A small but not unimportant detail!

Still curious how the hascrossedabove method works though? My interpretation of 'hascrossedabove' is that after crossing, the method will return true for an x amount of bars. X being the 'int Period' from the method. However, I do see an irregular amount 'true' when I code for this.


@duketv

duketv
14 Mar 2021, 21:15

RE:

Dear PanagiotisCharalampous
My interpretation of 'hascrossedabove' is that after crossing, the method will return true for an x amount of bars. X being the 'int Period' from the method. However, I do see an irregular amount 'true' when I code for this.  Perhaps my understanding is of the method is wrong? I coded a small bot to illustrate, being particularly charmed by the Schaff indicator myself, but any crossing indicator will work.

Ultimately my goal is to:

1. return true when price crosses and closes above MA for buy signal (and opposite for sell)

2. Return true for this instance for the next three bars or so (in case no direct overlap with other indicators)

 

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 NewcBot : Robot
    {
        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }


        [Parameter(Group = "Schaff")]
        public DataSeries SourceSchaff { get; set; }

        [Parameter("Short Cycle", DefaultValue = 28, Group = "Schaff")]
        public int ShortCycle { get; set; }

        [Parameter("Long Cycle", DefaultValue = 54, Group = "Schaff")]
        public int LongCycle { get; set; }

        [Parameter("Period", DefaultValue = 15, Group = "Schaff")]
        public int SchaffPeriod { get; set; }

        [Parameter("Factor", DefaultValue = 0.5, Group = "Schaff")]
        public double Factor { get; set; }

        [Parameter("Source SMA #1")]
        public DataSeries SourceSma1 { get; set; }

        [Parameter("Period SMA #1", DefaultValue = 5, MinValue = 1, MaxValue = 100)]
        public int PeriodsSma1 { get; set; }



        private SchaffTrendCycle _indi1 { get; set; }
        private SimpleMovingAverage _sma1 { get; set; }




        protected override void OnStart()
        {
            _indi1 = Indicators.GetIndicator<SchaffTrendCycle>(SourceSchaff, ShortCycle, LongCycle, SchaffPeriod, Factor);
            _sma1 = Indicators.SimpleMovingAverage(SourceSma1, PeriodsSma1);
        }

        protected override void OnTick()
        {
            // Put your core logic here
        }


        protected override void OnBar()
        {

            var SmaBuy = _sma1.Result.HasCrossedBelow(Symbol.Bid, 3);
            var SmaSell = _sma1.Result.HasCrossedAbove(Symbol.Bid, 3);
            var SchaffBuy = _indi1.Result.HasCrossedAbove(_indi1.L25, 3) && _indi1.Result.LastValue > 75;
            var SchaffSell = _indi1.Result.HasCrossedBelow(_indi1.L75, 3) && _indi1.Result.LastValue < 25;


            if (SchaffBuy == true)
            {
                Print("SchaffBuy = True");
            }

            if (SchaffSell == true)
            {
                Print("SchaffSell = True");
            }

            if (SmaBuy == true)
            {
                Print("SmaBuy = True");
            }

            if (SmaSell == true)
            {
                Print("SmaSell = True");
            }


        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
    }
}

 


@duketv

duketv
01 Dec 2020, 22:02

you can save individual pass parameters in an external file on your computer. Does that help?


@duketv

duketv
01 Dec 2020, 21:50

RE: RE:

@prosteel1

YOU ARE A GENIUS

exactly what I was looking for (and actually very simple). Many thanks


@duketv

duketv
01 Dec 2020, 21:47

RE:

xabbu said:

hey duketv,

you can devide your initial position in x smaller sub-portions and add different TP levels on each off them. This is how I manage it the NNFX way off doing fx...

 

Hi Xabbu,

You discovered my reason for wanting this in the first place ;-) I figured out later that I could circumvent this by opening two positions, but it does make it messier in the results. Anyway good idea. 

ps. I tried the Bressert Inidicator yesterday and worked quite well :)


@duketv

duketv
01 Dec 2020, 21:43

RE: RE:

paanoik@seznam.cz said:

Hi,

I don't get what do you mean, example what you want to achieve could help. Position can obviously has only one TP level. If it hit the level, position is closed. If you want tu change TP value, you can use method

ModifyPosition(position, SL, TP)

T.

Hi Paanoik,

My idea was to set two different take profit levels, so sell half of my position when it hits TP level 1 and the other half when it hits TP level 2. It looks like Prosteel1 has given a good example


@duketv

duketv
04 Nov 2020, 11:47

RE: ModifyPosition() example

prosteel1 said:

I think this is what you were asking for:

You can also paste this special line in OnStart so it calls the PositionsOnOpened when a Position is opened:

Positions.Opened += PositionsOnOpened;

Then you have a new method: that get run each time a position is opened which can modify stoploss etc. I use the comment to store the entry price, stoploss, take profit etc

private void PositionsOnOpened(PositionOpenedEventArgs args)
{
     var pos = args.Position;

       if (pos.Comment != "")
       {

                var splitted = pos.Comment.Split(' ');
                double ep = Convert.ToDouble(splitted[2]);
                double sl = Convert.ToDouble(splitted[4]);
                double tp = Convert.ToDouble(splitted[5]);
                if (pos.StopLoss != sl || pos.TakeProfit != tp)
                {
                    ModifyPosition(pos, sl, tp);

                }

      }

}

Hi @prosteel1

Thank you for your elaborate reply! Really appreciated. And slippage is not exactly the reason I am trying to do this. I am more a beginning coder/trader who wants to set dynamic stop loss that respond to market fluctuations, rather than setting a specefic amount of pips.

Your solution on working with comments looks very elegant, but I couldn't make it work. I think were it went wrong is that I made a seperate variable called "stopLossPips" (to calcluate a stop  loss based on Bollinger Bands) but by doing that I couldn't connect this to the variable "sl" that you suggested in your code.

I may have found a new solution (still testing it), but your 'comment' solution looks a lot easier. Would gladly learn more from it.

Thanks again for your replies, really happy with that!

var bbTop = BollingerBands.Top.LastValue;
var bbBottom = BollingerBands.Bottom.LastValue;
var bbMain = BollingerBands.Main.LastValue;
var stopLossPips = (bbMain - bbBottom) / Symbol.PipSize

 

 


@duketv

duketv
26 Oct 2020, 19:29

RE:

Hi @PanagiotisCharalampous
 

I think I see what you mean, but I have no clue how to solve right now.. Could you help? I spend almost 20 hours working on this problem, learned a lot! But no clue anymore how to solve it.

Regards,

Duke


@duketv

duketv
23 Oct 2020, 17:09

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



/*
Simple Cbot with MACD and a long period Moving Average for entry signals. Bollilinger Band is used for stop Loss
 */


namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class GardenStrategywithBBandtrailingstop : Robot
    {
        #region User defined parameters

        [Parameter("Instance Name", DefaultValue = "001")]
        public string InstanceName { get; set; }

        [Parameter("Lot Size", DefaultValue = 0.01)]
        public double LotSize { get; set; }

        [Parameter("Source SMA #1")]
        public DataSeries SourceSma1 { get; set; }

        [Parameter("Period SMA #1", DefaultValue = 160, MinValue = 1, MaxValue = 300)]
        public int PeriodsSma1 { get; set; }

        [Parameter(DefaultValue = MovingAverageType.Simple)]
        public MovingAverageType MaType { get; set; }

        [Parameter("Period MACD", DefaultValue = 9)]
        public int Period { get; set; }

        [Parameter("Long Cycle MACD", DefaultValue = 26)]
        public int LongCycle { get; set; }

        [Parameter("Short Cycle MACD", DefaultValue = 12)]
        public int ShortCycle { get; set; }

        [Parameter("Periods", DefaultValue = 20, Group = "Bollinger Bands")]
        public int BbPeriods { get; set; }

        [Parameter("Standart Dev", DefaultValue = 2, Group = "Bollinger Bands")]
        public double BbStdDev { get; set; }

        [Parameter("MA Type", DefaultValue = 0, Group = "Bollinger Bands")]
        public MovingAverageType BbMaType { get; set; }

        [Parameter("Source", Group = "Bollinger Bands")]
        public DataSeries BbSource { get; set; }

        [Parameter("Threshold MACD - Long", DefaultValue = 0, MinValue = -0.0003, MaxValue = 0, Step = 1E-05)]
        public double ThresLong { get; set; }

        [Parameter("Threshold MACD - Short", DefaultValue = 0, MinValue = 0, MaxValue = 0.0003, Step = 1E-05)]
        public double ThresShort { get; set; }

        [Parameter("Calculate OnBar", DefaultValue = false)]
        public bool CalculateOnBar { get; set; }

        [Parameter("Include Trailing Stop", DefaultValue = true)]
        public bool IncludeTrailingStop { get; set; }

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

        [Parameter("Trailing Stop Step (pips)", DefaultValue = 7)]
        public int TrailingStopStep { get; set; }

        [Parameter("Include Break-Even", DefaultValue = false)]
        public bool IncludeBreakEven { get; set; }

        [Parameter("Break-Even Trigger (pips)", DefaultValue = 10, MinValue = 1)]
        public int BreakEvenPips { get; set; }

        [Parameter("Break-Even Extra (pips)", DefaultValue = 2, MinValue = 1)]
        public int BreakEvenExtraPips { get; set; }

        #endregion

        #region Indicator declarations

        private SimpleMovingAverage _sma1 { get; set; }
        private MacdCrossOver Macd { get; set; }
        private BollingerBands BollingerBands { get; set; }

        #endregion

        #region cTrader events

        /// <summary>
        /// This is called when the robot first starts, it is only called once.
        /// </summary>
        protected override void OnStart()
        {
            // construct the indicators
            _sma1 = Indicators.SimpleMovingAverage(SourceSma1, PeriodsSma1);
            Macd = Indicators.MacdCrossOver(LongCycle, ShortCycle, Period);
            BollingerBands = Indicators.BollingerBands(Bars.ClosePrices, BbPeriods, BbStdDev, BbMaType);
            double Bid = Symbol.Bid;
            double Point = Symbol.TickSize;
        }

        /// <summary>
        /// This method is called every time the price changes for the symbol
        /// </summary>
        protected override void OnTick()
        {


            if (CalculateOnBar)
            {
                return;
            }

            ManagePositions();

            if (IncludeTrailingStop)
            {
                SetTrailingStop();
            }

            if (IncludeBreakEven)
            {
                BreakEvenAdjustment();
            }

        }

        /// <summary>
        /// This method is called at every candle (bar) close, when it has formed
        /// </summary>
        protected override void OnBar()
        {
            if (!CalculateOnBar)
            {
                return;
            }

            ManagePositions();
        }

        /// <summary>
        /// This method is called when your robot stops, can be used to clean-up memory resources.
        /// </summary>
        protected override void OnStop()
        {
            // unused
        }

        #endregion

        #region Position management

        public void ManagePositions()
        {
            var bbTop = BollingerBands.Top.LastValue;
            var bbBottom = BollingerBands.Bottom.LastValue;
            var bbMain = BollingerBands.Main.LastValue;

            if (_sma1.Result.LastValue < Symbol.Bid & Macd.MACD.Last(0) > Macd.Signal.Last(0) & Macd.MACD.Last(0) < ThresLong)
            {
                if (!IsPositionOpenByType(TradeType.Buy))
                {
                    var stopLossPips = (bbMain - bbBottom) / Symbol.PipSize;
                    OpenPosition(TradeType.Buy, stopLossPips);
                }

                else if (_sma1.Result.LastValue > Symbol.Bid & Macd.MACD.Last(0) < Macd.Signal.Last(0) & Macd.MACD.Last(0) > ThresShort)
                {
                    if (!IsPositionOpenByType(TradeType.Sell))
                    {
                        var stopLossPips = (bbTop - bbMain) / Symbol.PipSize;
                        OpenPosition(TradeType.Sell, stopLossPips);

                    }

                }
            }
        }


        /// <summary>
        /// Opens a new long position
        /// </summary>
        /// <param name="type"></param>
        private void OpenPosition(TradeType type)
        {
            // calculate volume from lot size.
            double volume = Symbol.QuantityToVolumeInUnits(LotSize);


            // open a new position
            ExecuteMarketOrder(type, this.SymbolName, volume, InstanceName, null, null);
        }





        /// <summary>
        /// closes position
        /// </summary>
        /// <param name="type"></param>
        private void ClosePosition(TradeType type)
        {
            var p = Positions.Find(InstanceName, this.SymbolName, type);

            if (p != null)
            {
                ClosePosition(p);
            }
        }

        #endregion

        #region Position Information

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private bool IsPositionOpenByType(TradeType type)
        {
            var p = Positions.FindAll(InstanceName, SymbolName, type);

            if (p.Count() >= 1)
            {
                return true;
            }

            return false;
        }

        #endregion

        #region Trailing Stop

        /// <summary>
        /// When the profit in pips is above or equal to Trigger the stop loss will start trailing the spot price.
        /// TrailingStop defines the number of pips the Stop Loss trails the spot price by. 
        /// If Trigger is 0 trailing will begin immediately. 
        /// </summary>
        private void SetTrailingStop()
        {
            var sellPositions = Positions.FindAll(InstanceName, SymbolName, TradeType.Sell);

            foreach (Position position in sellPositions)
            {
                double distance = position.EntryPrice - Symbol.Ask;

                if (distance < TrailingStopTrigger * Symbol.PipSize)
                    continue;

                double newStopLossPrice = Symbol.Ask + TrailingStopStep * Symbol.PipSize;

                if (position.StopLoss == null || newStopLossPrice < position.StopLoss)
                {
                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                }
            }

            var buyPositions = Positions.FindAll(InstanceName, SymbolName, TradeType.Buy);

            foreach (Position position in buyPositions)
            {
                double distance = Symbol.Bid - position.EntryPrice;

                if (distance < TrailingStopTrigger * Symbol.PipSize)
                    continue;

                double newStopLossPrice = Symbol.Bid - TrailingStopStep * Symbol.PipSize;
                if (position.StopLoss == null || newStopLossPrice > position.StopLoss)
                {
                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                }
            }
        }

        #endregion

        #region Break Even

        /// <summary>
        /// Adjusts the break even plus (x) pips
        /// </summary>
        /// <param name="tradeType"></param>
        private void BreakEvenAdjustment()
        {
            var allPositions = Positions.FindAll(InstanceName, SymbolName);

            foreach (Position position in allPositions)
            {
                if (position.StopLoss != null)
                    return;

                var entryPrice = position.EntryPrice;
                var distance = position.TradeType == TradeType.Buy ? Symbol.Bid - entryPrice : entryPrice - Symbol.Ask;

                // move stop loss to break even plus and additional (x) pips
                if (distance >= BreakEvenPips * Symbol.PipSize)
                {
                    if (position.TradeType == TradeType.Buy)
                    {
                        if (position.StopLoss <= position.EntryPrice + (Symbol.PipSize * BreakEvenExtraPips) || position.StopLoss == null)
                        {
                            ModifyPosition(position, position.EntryPrice + (Symbol.PipSize * BreakEvenExtraPips), position.TakeProfit);
                            Print("Stop Loss to Break Even set for BUY position {0}", position.Id);
                        }
                    }
                    else
                    {
                        if (position.StopLoss >= position.EntryPrice - (Symbol.PipSize * BreakEvenExtraPips) || position.StopLoss == null)
                        {
                            ModifyPosition(position, entryPrice - (Symbol.PipSize * BreakEvenExtraPips), position.TakeProfit);
                            Print("Stop Loss to Break Even set for SELL position {0}", position.Id);
                        }
                    }
                }
            }
        }

        #endregion
    }
}

 

 


@duketv

duketv
23 Oct 2020, 16:40

RE:

PanagiotisCharalampous said:

Hi duketv,

In my second I worked with OpenPosition, but this gives me problems in the method definition. It gives the error that no method has been found to override..

Then skip the override keyword :)

Best Regards,

Panagiotis 

Join us on Telegram

Ha! :) yes, I did think about that, but then I get the error that "No override for method OpenPosision requires 2 arguments"

Am I missing something ?

Have a good weekend!


@duketv

duketv
22 Oct 2020, 19:36

RE:

Hi @PanagiotisCharalampous
It took me quite some hours but I have found 2 possible solutions, although neither of them work correctly yet. Again, any input is greatly appreciated.

In my first try I created an extra method to deal with the ModifyStopLossPrice. It does this correctly, but it does this continously! And so Stoploss is continiously set at a different level, creating a loop, until take-profit has been reached. My wish is to have it set only once.

In my second I worked with OpenPosition, but this gives me problems in the method definition. It gives the error that no method has been found to override..

Please advise

Thank you

        protected override void PositionOnOpened(Position openedPosition)
        {
            _position = openedPosition;
            var bbTop = BollingerBands.Top.LastValue;
            var bbBottom = BollingerBands.Bottom.LastValue;
            var bbMain = BollingerBands.Main.LastValue;
            var stopLossPipsbuy = (bbMain - bbBottom) / Symbol.PipSize;
            var stopLossPipssell = (bbMain - bbBottom) / Symbol.PipSize;

            if (_position.TradeType == TradeType.Buy)
            {
                Trade.ModifyPosition(openedPosition, stopLossPipsbuy, null);
            }
            if (_position.TradeType == TradeType.Sell)
            {
                Trade.ModifyPosition(openedPosition, stopLossPipssell, null);
            }
        }

        public override void ManagePositions()
        {
            var bbTop = BollingerBands.Top.LastValue;
            var bbBottom = BollingerBands.Bottom.LastValue;
            var bbMain = BollingerBands.Main.LastValue;

            if (_sma1.Result.LastValue < Symbol.Bid & Macd.MACD.Last(0) > Macd.Signal.Last(0) & Macd.MACD.Last(0) < ThresLong)
            {
                if (!IsPositionOpenByType(TradeType.Buy))
                {
                    var stopLossPips = (bbMain - bbBottom) / Symbol.PipSize;
                    OpenPosition(TradeType.Buy, stopLossPips);
                }

                else if (_sma1.Result.LastValue > Symbol.Bid & Macd.MACD.Last(0) < Macd.Signal.Last(0) & Macd.MACD.Last(0) > ThresShort)
                {
                    if (!IsPositionOpenByType(TradeType.Sell))
                    {
                        var stopLossPips = (bbTop - bbMain) / Symbol.PipSize;
                        OpenPosition(TradeType.Sell, stopLossPips);

                    }

                }
            }
        }

 


@duketv

duketv
21 Oct 2020, 12:07

RE:

Hi @PanagiotisCharalampous,

Thank you for your incredible quick reply :)

Good to point out that I should divide the pipsize or use a different method. However, I get the error "Name ModifyStopLossPrice does not exist in current context" I suppose my reference to the position is wrong?

Thank you

Duke

 

        #region Position management

        private void ManagePositions()
        {
            double bbTop = BollingerBands.Top.LastValue;
            double bbBottom = BollingerBands.Bottom.LastValue;
            double bbMain = BollingerBands.Main.LastValue;

            if (_sma1.Result.LastValue < Symbol.Bid & Macd.MACD.Last(0) > Macd.Signal.Last(0) & Macd.MACD.Last(0) < ThresLong)
            {
                // if there is no buy position open, open one and close any sell position that is open
                if (!IsPositionOpenByType(TradeType.Buy))
                {
                    OpenPosition(TradeType.Buy);
                    ModifyStopLossPrice(bbBottom);
                }


            }

            // if a sell position is already open and signal is buy do nothing
            else if (_sma1.Result.LastValue > Symbol.Bid & Macd.MACD.Last(0) < Macd.Signal.Last(0) & Macd.MACD.Last(0) > ThresShort)
            {
                // if there is no sell position open, open one and close any buy position that is open
                if (!IsPositionOpenByType(TradeType.Sell))
                {
                    ModifyStopLossPrice(bbTop);
                }


            }
        }

        /// <summary>
        /// Opens a new long position
        /// </summary>
        /// <param name="type"></param>
        private void OpenPosition(TradeType type)
        {
            // calculate volume from lot size.
            double volume = Symbol.QuantityToVolumeInUnits(LotSize);


            // open a new position
            ExecuteMarketOrder(type, this.SymbolName, volume, InstanceName, null, null);

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        private void ClosePosition(TradeType type)
        {
            var p = Positions.Find(InstanceName, this.SymbolName, type);

            if (p != null)
            {
                ClosePosition(p);
            }
        }

        #endregion

        #region Position Information

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private bool IsPositionOpenByType(TradeType type)
        {
            var p = Positions.FindAll(InstanceName, SymbolName, type);

            if (p.Count() >= 1)
            {
                return true;
            }

            return false;
        }

        #endregion

 


@duketv

duketv
21 Sep 2020, 22:54

Hi PanagiotisCharalampous

 

Thanks for the reply again.

I am still in a beginning stage and learning to code so seeing pieces of code being used in a bot or indicator helps me to read and understand its use better. The bot I am trying to conjure up relies on MACD and a SMA to look for entry signals. I found a snippet of code for a trailing stop which I happily added and now I am trying to set the bottom and top lines of the Bollinger Bands as my stop losses, but that is where I hit a wall.

The code compiels but no orders are executed (backtesting on EURUSD M15), This set up is quite generic and should detect entry signals at least once a day. So if I don't see any trades in my backtest, I know it is not working correctly.

Your help is much appreciated.

Cheers

 

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


/*
Simple Cbot with MACD and a long period Moving Average for entry signals. Bollilinger Band is used for stop Loss
 */

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class GardenStrategy : Robot
    {
        #region User defined parameters

        [Parameter("Instance Name", DefaultValue = "001")]
        public string InstanceName { get; set; }

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

        [Parameter("Source SMA #1")]
        public DataSeries SourceSma1 { get; set; }

        [Parameter("Period SMA #1", DefaultValue = 200, MinValue = 1, MaxValue = 300)]
        public int PeriodsSma1 { get; set; }

        [Parameter(DefaultValue = MovingAverageType.Simple)]
        public MovingAverageType MaType { get; set; }

        [Parameter("Period MACD", DefaultValue = 9)]
        public int Period { get; set; }

        [Parameter("Long Cycle MACD", DefaultValue = 26)]
        public int LongCycle { get; set; }

        [Parameter("Short Cycle MACD", DefaultValue = 12)]
        public int ShortCycle { get; set; }

        [Parameter("Periods", DefaultValue = 20, Group = "Bollinger Bands")]
        public int BbPeriods { get; set; }

        [Parameter("Standart Dev", DefaultValue = 2, Group = "Bollinger Bands")]
        public double BbStdDev { get; set; }

        [Parameter("MA Type", DefaultValue = 0, Group = "Bollinger Bands")]
        public MovingAverageType BbMaType { get; set; }

        [Parameter("Source", Group = "Bollinger Bands")]
        public DataSeries BbSource { get; set; }

        [Parameter("Include Trailing Stop", DefaultValue = true)]
        public bool IncludeTrailingStop { get; set; }

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

        [Parameter("Trailing Stop Step (pips)", DefaultValue = 7)]
        public int TrailingStopStep { get; set; }



        #endregion

        #region Indicator declarations

        private SimpleMovingAverage _sma1 { get; set; }
        private BollingerBands BollingerBands { get; set; }
        private MacdCrossOver Macd { get; set; }

        #endregion

        #region cTrader events

        /// <summary>
        /// This is called when the robot first starts, it is only called once.
        /// </summary>
        protected override void OnStart()
        {
            // construct the indicators
            _sma1 = Indicators.SimpleMovingAverage(SourceSma1, PeriodsSma1);
            Macd = Indicators.MacdCrossOver(LongCycle, ShortCycle, Period);
            double Bid = Symbol.Bid;
            double Point = Symbol.TickSize;
        }


        protected override void OnTick()
        {

        }


        protected override void OnBar()
        {
            var bbTop = BollingerBands.Top.LastValue;
            var bbMain = BollingerBands.Main.LastValue;
            var bbBottom = BollingerBands.Bottom.LastValue;

            if (_sma1.Result.LastValue < Symbol.Bid & Macd.MACD.Last(0) > Macd.Signal.Last(0) & Macd.MACD.Last(0) < 0)
                ExecuteMarketOrder(TradeType.Buy, this.SymbolName, Volume, InstanceName, null, null);
            Positions[0].ModifyStopLossPrice(bbBottom);

            if (_sma1.Result.LastValue > Symbol.Bid & Macd.MACD.Last(0) < Macd.Signal.Last(0) & Macd.MACD.Last(0) > 0)
                ExecuteMarketOrder(TradeType.Sell, this.SymbolName, Volume, InstanceName, null, null);
            Positions[0].ModifyStopLossPrice(bbTop);
        }

        protected override void OnStop()
        {

        }

        #endregion


        #region Trailing Stop

        /// <summary>
        /// When the profit in pips is above or equal to Trigger the stop loss will start trailing the spot price.
        /// TrailingStop defines the number of pips the Stop Loss trails the spot price by. 
        /// If Trigger is 0 trailing will begin immediately. 
        /// </summary>
        private void SetTrailingStop()
        {
            var sellPositions = Positions.FindAll(InstanceName, SymbolName, TradeType.Sell);

            foreach (Position position in sellPositions)
            {
                double distance = position.EntryPrice - Symbol.Ask;

                if (distance < TrailingStopTrigger * Symbol.PipSize)
                    continue;

                double newStopLossPrice = Symbol.Ask + TrailingStopStep * Symbol.PipSize;

                if (position.StopLoss == null || newStopLossPrice < position.StopLoss)
                {
                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                }
            }

            var buyPositions = Positions.FindAll(InstanceName, SymbolName, TradeType.Buy);

            foreach (Position position in buyPositions)
            {
                double distance = Symbol.Bid - position.EntryPrice;

                if (distance < TrailingStopTrigger * Symbol.PipSize)
                    continue;

                double newStopLossPrice = Symbol.Bid - TrailingStopStep * Symbol.PipSize;
                if (position.StopLoss == null || newStopLossPrice > position.StopLoss)
                {
                    ModifyPosition(position, newStopLossPrice, position.TakeProfit);
                }
            }
        }

        #endregion

    }
}

 


@duketv

duketv
18 Sep 2020, 13:46

Hi @PanagiotisCharalampous

Thank you for taking the time to reply to my query.

Do you have or know of sample code with ModifyStopLossPrice? I cannot find any snippets on the forum or in the offered robots, but I am sure it must be there..

I did code a workaround for a simple strategy, but it doesn't execute any order. Any ideas?

Many thanks and best wishes for the weekend
 

        protected override void OnBar()
        {
            var bbTop = BollingerBands.Top.LastValue;
            var bbMain = BollingerBands.Main.LastValue;
            double StopLossPips = (bbTop - bbMain) / Symbol.PipSize;

            if (_sma1.Result.LastValue < Symbol.Bid & Macd.MACD.Last(0) > Macd.Signal.Last(0) & Macd.MACD.Last(0) < 0)
                ExecuteMarketOrder(TradeType.Buy, this.SymbolName, Volume, InstanceName, StopLossPips, null);

            if (_sma1.Result.LastValue > Symbol.Bid & Macd.MACD.Last(0) < Macd.Signal.Last(0) & Macd.MACD.Last(0) > 0)
                ExecuteMarketOrder(TradeType.Sell, this.SymbolName, Volume, InstanceName, StopLossPips, null);

 


@duketv