Problem with custom library

Created at 10 Mar 2024, 20:53
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!
jani's avatar

jani

Joined 05.04.2019

Problem with custom library
10 Mar 2024, 20:53


I want to build a library to consolidate some of my custom indicators. I am using VS 2022 and cTrader version 4.8.30.213183. I was able to construct the library and I added the reference to the cBot.  I was able to build/compile the cBot and the Library in VS 2022 just fine. But when I tried to backtest the cBot I got an error message in cTrader Automate Build:

However, VS 2022 seems to build the code fine as I get:

1>C:\Users\janiv\OneDrive\Documents\cAlgo\CustomLibraries\CustomIndicatorLibrary\CustomIndicatorLibrary\Class1.cs(40,41,40,50): warning CS8618: Non-nullable field 'trueRange' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
1>C:\Users\janiv\OneDrive\Documents\cAlgo\CustomLibraries\CustomIndicatorLibrary\CustomIndicatorLibrary\Class1.cs(41,41,41,44): warning CS8618: Non-nullable field 'atr' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
1>CustomIndicatorLibrary -> C:\Users\janiv\OneDrive\Documents\cAlgo\CustomLibraries\CustomIndicatorLibrary\CustomIndicatorLibrary\bin\Debug\net6.0\CustomIndicatorLibrary.dll
1>Done building project "CustomIndicatorLibrary.csproj".
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
========== Rebuild started at 10:32 PM and took 00,563 seconds ==========
 

I got some warnings but the build went through fine.

 

cBot code:

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

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

        [Parameter(DefaultValue = "SuperTrendReference_cBot")]
        public string cBotLabel { get; set; }

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

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

        [Parameter("Slow SMA Period", DefaultValue = 21)]
        public int SlowSMAPeriod { get; set; }
        
        [Parameter("KAMA Period", DefaultValue = 10)]
        public int KaMaPeriod { get; set; }

        [Parameter("Take Profit (pips):", DefaultValue = 50.0)]
        public double TakeProfit { get; set; }

        [Parameter("Stop Loss (pips):", DefaultValue = 50.0)]
        public double StopLoss { get; set; }
               
          
        private MovingAverage fastSMA;
        private MovingAverage slowSMA;
        private CustomIndicatorLibrary.Class1.KAMA KAMA_;

        protected override void OnStart()
        {            
            fastSMA = Indicators.MovingAverage(Bars.ClosePrices, FastSMAPeriod, MovingAverageType.Simple);
            slowSMA = Indicators.MovingAverage(Bars.ClosePrices, SlowSMAPeriod, MovingAverageType.Simple);


            // Create an instance of the CustomKAMA indicator
            KAMA_ = Indicators.GetIndicator<CustomIndicatorLibrary.Class1.KAMA>(
                Symbol.Name,
                TimeFrame,
                new CustomIndicatorLibrary.Class1.KAMA.Parameters
                {
                    Source = Bars.ClosePrices,
                    Period = KaMaPeriod,
                    Fast = 2,
                    Slow = 30,
                    ATRPeriod = 14,
                    BufferFactor = 1
                });
            
            
        }

        protected override void OnBar()
        {                      
            
            var currentFastSMA = fastSMA.Result.LastValue;
            var currentSlowSMA = slowSMA.Result.LastValue;

            var previousFastSMA = fastSMA.Result.Last(1);
            var previousSlowSMA = slowSMA.Result.Last(1);
                
                
               // Check for a crossover
            if (currentFastSMA > currentSlowSMA && previousFastSMA <= previousSlowSMA)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, Volume, "SMA Crossover - Buy", StopLoss, TakeProfit);
            }
            else if (currentFastSMA < currentSlowSMA && previousFastSMA >= previousSlowSMA)
            {
     
                ExecuteMarketOrder(TradeType.Sell, SymbolName, Volume, "SMA Crossover - Sell", StopLoss, TakeProfit);
            }  
                
                

        }
        private void OpenMarketOrder(TradeType tradeType, double dLots, double takeProfit, double stopLoss)
        {
            var volumeInUnits = Symbol.QuantityToVolumeInUnits(dLots);
            volumeInUnits = Symbol.NormalizeVolumeInUnits(volumeInUnits, RoundingMode.Down);

         
            var result = ExecuteMarketOrder(tradeType, Symbol.Name, volumeInUnits, cBotLabel, takeProfit, stopLoss);
            if (!result.IsSuccessful)
            {
                Print("Execute Market Order Error: {0}", result.Error.Value);
                OnStop();
            }
        }

        private int CalculatePositionsQnty(TradeType tradeType)
        {
            return Positions.FindAll(cBotLabel, Symbol.Name, tradeType).Length;
        }

        private void CloseAllPositions(TradeType tradeType)
        {
            foreach (var position in Positions.FindAll(cBotLabel, Symbol.Name, tradeType))
            {
                var result = ClosePosition(position);
                if (!result.IsSuccessful)
                {
                    Print("Closing market order error: {0}", result.Error);
                }
            }
        }
    }
}

library code:

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

namespace CustomIndicatorLibrary
{
    public class Class1
    {
        [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
        public class KAMA : Indicator
        {
            [Parameter()]
            public DataSeries Source { get; set; }

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

            [Parameter("Fast MA", DefaultValue = 2)]
            public double Fast { get; set; }

            [Parameter("Slow MA", DefaultValue = 30)]
            public int Slow { get; set; }

            [Parameter("ATR Period", DefaultValue = 14)]
            public int ATRPeriod { get; set; }

            [Parameter("Buffer Factor", DefaultValue = 1)]
            public double BufferFactor { get; set; }

            [Output("KAMA", PlotType = PlotType.Line, LineColor = "Lime", Thickness = 2)]
            public IndicatorDataSeries Kama { get; set; }

            [Output("Up Buffer", PlotType = PlotType.Line, LineColor = "White", Thickness = 1, LineStyle = LineStyle.Lines)]
            public IndicatorDataSeries UpBuff { get; set; }

            [Output("Down Buffer", PlotType = PlotType.Line, LineColor = "White", Thickness = 1, LineStyle = LineStyle.Lines)]
            public IndicatorDataSeries DownBuff { get; set; }

            private IndicatorDataSeries diff;
            private IndicatorDataSeries trueRange;
            private IndicatorDataSeries atr;
            double fastd;
            double slowd;

            protected override void Initialize()
            {
                diff = CreateDataSeries();
                trueRange = CreateDataSeries();
                atr = CreateDataSeries();
                fastd = 2.0 / (Fast + 1);
                slowd = 2.0 / (Slow + 1);
            }

            public override void Calculate(int index)
            {
                if (index >= 1)
                {
                    diff[index] = Math.Abs(Source[index] - Source[index - 1]);
                    trueRange[index] = Math.Max(Math.Max(Bars.HighPrices[index] - Bars.LowPrices[index], Math.Abs(Bars.HighPrices[index] - Bars.ClosePrices[index - 1])), Math.Abs(Bars.LowPrices[index] - Bars.ClosePrices[index - 1]));

                    // Simple Moving Average for ATR
                    if (index >= ATRPeriod)
                    {
                        double atrSum = 0;
                        for (int i = index - ATRPeriod + 1; i <= index; i++)
                        {
                            atrSum += trueRange[i];
                        }
                        atr[index] = atrSum / ATRPeriod;
                    }
                }

                if (index < Period)
                {
                    Kama[index] = Source[index];
                    return;
                }

                double signal = Math.Abs(Source[index] - Source[index - Period]);
                double noise = 0.0;
                for (int i = index - Period + 1; i <= index; i++)
                {
                    noise += diff[i];
                }

                if (noise == 0)
                {
                    Kama[index] = Kama[index - 1];
                }
                else
                {
                    double smoothingFactor = Math.Pow(((signal / noise) * (fastd - slowd) + slowd), 2);
                    Kama[index] = Kama[index - 1] + smoothingFactor * (Source[index] - Kama[index - 1]);
                }

                if (index >= ATRPeriod)
                {
                    UpBuff[index] = Kama[index] + atr[index] * BufferFactor;
                    DownBuff[index] = Kama[index] - atr[index] * BufferFactor;
                }
            }
        }

       
   
    }
}

 

cBot references should be just fine:

 

Could someone help me with this issue?


@jani
Replies

jani
10 Mar 2024, 22:43

Sorry could not find the way to delete this thread. I solved the issue already. It was a problem with:
 

new CustomIndicatorLibrary.Class1.KAMA.Parameters

just needed to remove the "Parameters" and simplify the syntax

 

 

 

 


@jani