Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

Cyclic Smoothed RSI V2 for NT8 - Help

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Cyclic Smoothed RSI V2 for NT8 - Help

    I have been using this indicator on TradingView for some time now and thought I would try my hand at converting it. I have experience with creating ThinkOrSwim indicators as well as TradingView, but have never really done a NinjaScript indicator from scratch. It is proving extremely difficult. I am spending more time searching for code examples than I am writing it. I tried to do my best, but could really use some help with this one. The code I cam up with doesn't even plot a line on the chart although it compiles completely.

    The code I am trying to convert from TradingView is here:
    Cyclic Smoothed Relative Strength Indicator The cyclic smoothed RSI indicator is an enhancement of the classic RSI , adding additional smoothing according to the market vibration, adaptive upper and lower bands according to the cyclic memory and using the current dominant cycle length as input for the indicator. The cRSI is used like a standard indicator. The chart highlights trading signals where the signal line crosses above or below the adaptive lower/upper bands. It is much more …


    The same code can be found for ThinkOrSwim here:
    I originally found this little beauty on TradingView, written by WhenToTrade. It combines the dominant cycle length to create adaptive RSI overbought/oversold bands for more responsive signals. # Cyclic RSI # Origanlly found on Tradingview: https://www.tradingview.com/v/TmqiR1jp/ # Shout out...


    Any help is appreciated. I didn't include the "Do Not Touch" section due to character limits.

    The code I created is this:

    [CODE]region Using declarations
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Xml.Serialization;
    using NinjaTrader.Cbi;
    using NinjaTrader.Gui;
    using NinjaTrader.Gui.Chart;
    using NinjaTrader.Gui.SuperDom;
    using NinjaTrader.Gui.Tools;
    using NinjaTrader.Data;
    using NinjaTrader.NinjaScript;
    using NinjaTrader.Core.FloatingPoint;
    using NinjaTrader.NinjaScript.DrawingTools;
    #endregion

    //This namespace holds Indicators in this folder and is required. Do not change it.
    namespace NinjaTrader.NinjaScript.Indicators
    {
    public class CyclicSmoothedRSIV2 : Indicator
    {
    private Series<double> CyclicRSI;
    private Series<double> NormalRSI;
    private Series<double> Wilders;
    private Series<double> Src;
    private Series<double> Up;
    private Series<double> Down;
    private Series<double> Lm_Hist;
    private Series<double> DB;
    private Series<double> UB;
    private Series<double> CautionUp;
    private Series<double> CautionDown;
    private Series<double> CautionBase;

    protected override void OnStateChange()
    {
    if (State == State.SetDefaults)
    {
    Description = @"// Copyright (C) 2017 CC BY, whentotrade / Lars von Thienen
    Source:
    Book: Decoding The Hidden Market Rhythm - Part 1: Dynamic Cycles (2017)
    Chapter 4: "Fine-tuning technical indicators for more details on the cRSI Indicator

    Usage:
    You need to derive the dominant cycle as input parameter for the cycle length as described in chapter 4.

    License:
    This work is licensed under a Creative Commons Attribution 4.0 International License.
    You are free to share the material in any medium or format and remix, transform, and build upon the material for any purpose,
    even commercially. You must give appropriate credit to the authors book and website, provide a link to the license, and indicate
    if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.

    Modified by IMakeNoCents​";

    Name = "CyclicSmoothedRSIV2";
    Calculate = Calculate.OnBarClose;
    IsOverlay = false;
    DisplayInDataBox = true;
    DrawOnPricePanel = true;
    DrawHorizontalGridLines = true;
    DrawVerticalGridLines = true;
    PaintPriceMarkers = true;
    ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
    //Disable this property if your indicator requires custom values that cumulate with each new market data event.
    //See Help Guide for additional information.
    IsSuspendedWhileInactive = true;
    Domcycle = 28;
    RSI_Upper = 70;
    RSI_Lower = 30;
    ShowCyclicMidline = true;
    UseFullView = false;
    ShowRSImidline = false;
    ShowCaution = true;
    ShowClouds =true;
    AddPlot(Brushes.Aqua, "Lowband");
    AddPlot(Brushes.Aqua, "Highband");
    AddPlot(Brushes.WhiteSmoke, "CRSI");
    AddPlot(new Stroke(Brushes.MediumOrchid, 2), PlotStyle.Dot, "Centerline");
    AddPlot(new Stroke(Brushes.DarkGray, 2), PlotStyle.Hash, "Mid");


    }
    else if (State == State.Configure)
    {
    }
    else if (State == State.DataLoaded)
    {
    if(ShowRSImidline)
    {
    Mid[0] = 50;
    }

    if(UseFullView)
    {
    AddLine(Brushes.DarkRed, 100, "FullViewUpperLimit");
    AddLine(Brushes.LimeGreen, 0, "FullViewLowerLimit");
    }
    }
    }

    protected override void OnBarUpdate()
    {


    Src[0] = Close[0];
    CyclicRSI[0] = 0.0;
    double cyclelen = Domcycle / 2;
    int vibration = 10;
    double leveling = 10.0;
    int cyclicMemory = Domcycle * 2;

    int RSI_Center = 50;

    double torque = 2.0 / (vibration + 1);
    double phasingLag = (vibration - 1) / 2.0;

    double alpha = 1 / cyclelen;

    if(CurrentBar == 0)
    {
    Up[0] = 0.0;
    Down[0] = 0.0;
    Lm_Hist[0] = 0.0;
    NormalRSI[0] = 0.0;
    CyclicRSI[0] = 0.0;
    DB[0] = 0.0;
    UB[0] = 0.0;
    }
    else
    {
    Up[0] = alpha * Math.Max(Src[0] - Src[1], 0) + (1 - alpha) * Up[1];
    Down[0] = alpha * -Math.Min(Src[0] - Src[1], 0) + (1 - alpha) * Down[1];

    }

    if(CurrentBar > 0 && Down[0] == 0)
    {
    NormalRSI[0] = 100;
    }
    else if(CurrentBar > 0 && Up[0] == 0)
    {
    NormalRSI[0] = 0;
    }
    else
    {
    NormalRSI[0] = 100 - 100 / (1 + Up[0] / Down[0]);
    }

    if(CurrentBar > 0)
    {
    CyclicRSI[0] = torque * (2 * NormalRSI[0] - NormalRSI[4]) + (1 - torque) * CyclicRSI[1];
    }

    double CyclicMax = MAX(CyclicRSI, cyclicMemory - 1)[0];
    double CyclicMin = MIN(CyclicRSI, cyclicMemory -1)[0];
    if(CurrentBar > 0)
    {
    Lm_Hist[0] = CyclicRSI[0] > CyclicMax ? CyclicRSI[0] : -CyclicRSI[0] < CyclicMin ? -CyclicRSI[0] : 0;
    }

    double lmax = -MAX(Lm_Hist, cyclicMemory - 1)[0];
    double lmin = -MIN(Lm_Hist, cyclicMemory - 1)[0];
    double mstep = (lmax - lmin) / 100;
    double aperc = leveling / 100;

    for(int i = 0; i < 101; i++)
    {
    double testvalue = lmin + mstep * i;
    double below = 0;
    for(int j = 0; j < cyclicMemory; j++)
    {
    below += (CyclicRSI[j] < testvalue ? 1 : 0);
    }
    double ratio = below / cyclicMemory;
    if(ratio >= aperc)
    {
    DB[0] = testvalue;
    break;
    }
    else
    {
    continue;
    }
    }

    for(int i = 0; i < 101; i++)
    {
    double testvalue = lmax - mstep * i;
    double above = 0;
    for(int j = 0; j < cyclicMemory; j++)
    {
    above += (CyclicRSI[j] >= testvalue ? 1 : 0);
    }
    double ratio = above / cyclicMemory;
    if(ratio >= aperc)
    {
    UB[0] = testvalue;
    break;
    }
    else
    {
    continue;
    }
    }
    double center = (UB[0] + DB[0]) / 2;
    bool downTrend = CyclicRSI[0] < DB[0] ? true : false;
    bool upTrend = CyclicRSI[0] > UB[0] ? true : false;
    bool crossingDown = CrossBelow(CyclicRSI, UB, 0);
    bool crossingUp = CrossAbove(CyclicRSI, DB, 0);

    Lowband[0] = DB[0];
    Highband[0] = UB[0];
    CRSI[0] = CyclicRSI[0];
    Centerline[0] = center;

    if(ShowCaution)
    {
    int savedCautionBar = 0;

    if(DB[0] <= RSI_Lower && CRSI[0] < DB[0] && UB[0] < (RSI_Upper - ((100 - RSI_Upper) / 2)))
    {
    CautionUp[0] = 100;
    if(CrossAbove(CautionUp, 50, 0))
    {
    savedCautionBar = CurrentBar;
    }
    }
    else
    {
    CautionUp.Reset();
    }

    if(UB[0] >= RSI_Upper && CRSI[0] > UB[0] && DB[0] > (RSI_Lower + (RSI_Lower / 2)))
    {
    CautionDown[0] = 100;
    if(CrossBelow(CautionDown, 50, 0))
    {
    savedCautionBar = CurrentBar;
    }
    }
    else
    {
    CautionDown.Reset();
    }

    if(CautionUp[0] == 100 || CautionDown[0] == 100)
    {
    CautionBase[0] = 0;
    if(CautionUp[0] == 100)
    {
    Draw.Region(this, "Look for Reversals - Caution", CurrentBar - savedCautionBar + 1, 0, CautionBase, CautionUp, null, Brushes.Goldenrod, 30);
    }
    if(CautionDown[0] == 100)
    {
    Draw.Region(this, "Look for Reversals - Caution", CurrentBar - savedCautionBar + 1, 0, CautionBase, CautionDown ,null, Brushes.Goldenrod, 30);

    }
    }
    else
    {
    CautionBase.Reset();
    }
    }
    if(ShowClouds)
    {
    int savedCrossOverBar;
    }
    //END
    }

    region Properties
    [NinjaScriptProperty]
    [Range(10, int.MaxValue)]
    [Display(Name="Domcycle", Description="Dominant Cycle Length", Order=1, GroupName="Parameters")]
    public int Domcycle
    { get; set; }

    [NinjaScriptProperty]
    [Range(1, int.MaxValue)]
    [Display(Name="RSI_Upper", Description="Upper Limit - Normal RSI", Order=2, GroupName="Parameters")]
    public int RSI_Upper
    { get; set; }

    [NinjaScriptProperty]
    [Range(1, int.MaxValue)]
    [Display(Name="RSI_Lower", Description="Lower RSI - Normal Limit", Order=3, GroupName="Parameters")]
    public int RSI_Lower
    { get; set; }

    [NinjaScriptProperty]
    [Display(Name="ShowCyclicMidline", Description="Display the average of upper and lower bands", Order=4, GroupName="Parameters")]
    public bool ShowCyclicMidline
    { get; set; }

    [NinjaScriptProperty]
    [Display(Name="UseFullView", Description="Plot lines at 0 and 100 to show full view of indicator", Order=5, GroupName="Parameters")]
    public bool UseFullView
    { get; set; }

    [NinjaScriptProperty]
    [Display(Name="ShowRSImidline", Description="Plot a line at the 50 level of the indicator for reference", Order=6, GroupName="Parameters")]
    public bool ShowRSImidline
    { get; set; }

    [NinjaScriptProperty]
    [Display(Name="ShowCaution", Description="Highlights background when indicator is entering very overbought or oversold territory", Order=7, GroupName="Parameters")]
    public bool ShowCaution
    { get; set; }

    [NinjaScriptProperty]
    [Display(Name="ShowClouds", Description="Highlights area between CRSI and Highband or Lowband if trending above or below them", Order=8, GroupName="Parameters")]
    public bool ShowClouds
    { get; set; }


    [Browsable(false)]
    [XmlIgnore]
    public Series<double> Lowband
    {
    get { return Values[0]; }
    }

    [Browsable(false)]
    [XmlIgnore]
    public Series<double> Highband
    {
    get { return Values[1]; }
    }

    [Browsable(false)]
    [XmlIgnore]
    public Series<double> CRSI
    {
    get { return Values[2]; }
    }

    // [Browsable(false)]
    // [XmlIgnore]
    // public Series<double> CautionUp
    // {
    // get { return Values[3]; }
    // }

    // [Browsable(false)]
    // [XmlIgnore]
    // public Series<double> CautionDown
    // {
    // get { return Values[4]; }
    // }

    [Browsable(false)]
    [XmlIgnore]
    public Series<double> Mid
    {
    get { return Values[3]; }
    }

    [Browsable(false)]
    [XmlIgnore]
    public Series<double> Centerline
    {
    get { return Values[4]; }
    }

    // [Browsable(false)]
    // [XmlIgnore]
    // public Series<double> CautionBase
    // {
    // get { return Values[6]; }
    // }
    #endregion

    }
    }


    #2
    Try this code to verify that it shows the same result as in TradingView (only RSI Cyclic without bands).
    Only the programming of the bands remains pending (later I upload it)​.

    Code:
            [NinjaScriptProperty]
            [Range(10, Int32.MaxValue)]
            [Display(Name = "Dom Cycle", Description = "Dominant Cycle Length", Order = 10, GroupName = "NinjaScriptParameters")]
            public int DomCycle{ get; set; }
    
    
            private RSI rsi;
            private double torque;
            private int phasingLag; // Tiene que ser Int32 ya que es argumento para el período del RSI
    
            protected override void OnStateChange()
            {
                if (State == State.SetDefaults)
                {
                    Description = @"Enter the description for your new custom Indicator here.";
                    Name = "Zi8RSICyclicSmoothedV2";
                    Calculate = Calculate.OnBarClose;
                    IsOverlay = false;
                    DisplayInDataBox = true;
                    DrawOnPricePanel = true;
                    DrawHorizontalGridLines = true;
                    DrawVerticalGridLines = true;
                    PaintPriceMarkers = true;
                    ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
                    //Disable this property if your indicator requires custom values that cumulate with each new market data event.
                    //See Help Guide for additional information.
                    IsSuspendedWhileInactive = true;
    
    
                    DomCycle = 20;
    
                    AddLine(Brushes.DarkCyan, 30, NinjaTrader.Custom.Resource.NinjaScriptIndicatorLower);
                    AddLine(Brushes.DarkCyan, 70, NinjaTrader.Custom.Resource.NinjaScriptIndicatorUpper);
    
                    AddPlot(new Stroke(Brushes.DodgerBlue, 2.0F), PlotStyle.Line, "RSI Cyclic");
    
                }
                else if (State == State.DataLoaded)
                {
                    const int VIBRATION = 10;
    
                    torque = 2.0 / (VIBRATION + 1);
                    phasingLag = (VIBRATION - 1) / 2;
                    rsi = RSI(DomCycle / 2, 1);
                }
            }
    
            protected override void OnBarUpdate()
            {
                if (CurrentBar < phasingLag)
                    return;
    
                Value[0] = torque * (2 * rsi[0] - rsi[phasingLag]) + (1 - torque) * Value[1];
            }​

    Comment


      #3
      Here is the full code
      Attached Files

      Comment


        #4
        This is great! Thank you for the help. I noticed that in the calculation for the upper band, " i < 100 " should be " i <= 100 ", so I changed that. Also, I am getting different results for the plots compared to TradingView, but it's not that bad and I think I can correct it. I'll repost when I add my customizations, but I want to try to do them myself rather than asking for help yet again. Thank You!

        Comment


          #5
          Hello imakenocents,

          Thanks for your post.

          If an indicator is not behaving as expected, such as not plotting values on the chart window, you should note the Log tab of the Control Center for any error messages that may appear when running the script. Also, it is necessary to add debugging prints to the script to understand exactly how your logic is evaluating.

          Below is a link to a forum post that demonstrates how to use prints to understand behavior.
          https://ninjatrader.com/support/foru...121#post791121

          Further, in the script you shared, I see you are calling AddLine() when the State == State.DataLoaded. The AddLine() method should only be called in State.SetDefaults or State.Configure as noted in the AddLine() help guide page.

          AddLine(): https://ninjatrader.com/support/help...ine.htm​
          Brandon H.NinjaTrader Customer Service

          Comment


            #6
            Thanks Brandon, I was actually just reading that. The script that cis71 provided was great and fixed that. I am just trying to add some modifications like making those 30 / 70 lines able to be turned on or off, as well as a 50 line, and adding some auto color change to the main indicator plot. Then I'll add some fill when the main plot is over the highband or under the lowband. I am trying to learn as much as I can by attempting to do it myself, but this is so much different than PineScript or ThinkScript...or even javascript for that matter.
            I think the variations in values between TradingView and NT, for this indicator, are probably based on how the RMA function is calculated. The internal NT RSI calculation is similar but not quite the same.

            PineScript RMA function( na() just checks for NaN, so the first bar is an SMA):

            pine_rma(src, length) =>
            alpha = 1/length
            sum = 0.0
            sum := na(sum[1]) ? ta.sma(src, length) : alpha * src + (1 - alpha) * nz(sum[1])

            ​NT RSI calculation (constant3 is equal to (Period - 1)::

            avgDown[0] = (avgDown[1] * constant3 + down[0]) / Period;
            avgUp[0] = (avgUp[1] * constant3 + up[0]) / Period;

            There is more to it but from what I can see that is the main calculation. Not sure how I will fix it but I'll post back here if I figure it out. Thanks!

            Comment


              #7
              Hello imakenocents,

              Thanks for your notes.

              You could use Lines[int index].Brush to dynamically change the color of a line on the chart based on the indicator's value. For example, Lines[1].Brush = Brushes.Transparent; could be used to set the second line in the script to a transparent color when a certain condition occurs.

              Please see the sample code in this help guide page demonstrating how this could be accomplished: https://ninjatrader.com/support/help...html?lines.htm

              To change the color of the plot in the script when a certain condition occurs you could use PlotBrushes.

              See this help guide page for more information about PlotBrushes and sample code: https://ninjatrader.com/support/help...lotbrushes.htm
              Brandon H.NinjaTrader Customer Service

              Comment


                #8
                I just did this with a bool trigger in the inputs and a custom brush to change colors, seems to work well except I can't change the line type, but thats ok:

                else if (State == State.Configure)
                {
                if(ShowNormalRSI)
                {
                AddLine(NormalRSILines, 30, "Normal Lower RSI Line");
                AddLine(NormalRSILines, 70, "Normal Higher RSI Line");
                }
                if(ShowRSI_50)
                {
                AddLine(new Stroke(NormalRSILines, DashStyleHelper.Dash, 1), 50, "Normal RSI Mid-Line");
                }

                Comment


                  #9
                  Here is the latest version I was working on. The colors are better and there is an option to not view the normal RSI lines. I commented out what I was working on now, but I'll post when I get some more time to complete. I still need to get the cloud shading to work, but I'll get to that later. Thank you both for the help so far!
                  Attached Files

                  Comment


                    #10
                    Ok, so I am having a difficult time getting the shading to work, can anyone tell me what I am doing wrong here? The commented out "if (UseClouds)" code is what I have so far. Not sure if I need to create the cloud brushes since there is an option for opacity in Draw.Region, but i left it there just in case. Same with the series SavedBarUp & SavedBarDown

                    This is what it is supposed to look like:
                    Click image for larger version  Name:	image.png Views:	0 Size:	53.8 KB ID:	1265884

                    But this is what I am getting:
                    Click image for larger version  Name:	image.png Views:	0 Size:	45.3 KB ID:	1265885​​​

                    I an not concerned with the arrows.
                    Any help would be appreciated!
                    Last edited by NinjaTrader_BrandonH; 08-23-2023, 01:24 PM.

                    Comment


                      #11
                      I don't see the latest file posted so just adding it here.
                      Attached Files

                      Comment


                        #12
                        Hello imakenocents,

                        Thanks for your notes.

                        The Draw.Region() methods being called in the if (UseClouds) section of code have invalid arguments which is throwing compile errors in the script. See the errors below.
                        CyclicSmoothedRSIV2 (1).cs The best overloaded method match for 'NinjaTrader.NinjaScript.DrawingTools.Draw.Region( NinjaTrader.NinjaScript.NinjaScriptBase, string, int, int, NinjaTrader.NinjaScript.ISeries<double>, NinjaTrader.NinjaScript.ISeries<double>, System.Windows.Media.Brush, System.Windows.Media.Brush, int, int)' has some invalid arguments CS1502 380 6
                        CyclicSmoothedRSIV2 (1).cs Argument 9: cannot convert from 'double' to 'int' CS1503 380 111

                        The areaOpacity argument requires an int value to be passed in but in your code, you are passing in a double value (CloudOpacity).

                        To resolve the compile errors, you would need to make sure that you are passing in an int argument for the areaOpacity argument of Draw.Region(). For example, you would need to change the CloudOpacity property from a double to an int and make sure that an int (whole number) is being assigned to the CloudOpacity property.

                        See this help guide page for more information about using the Draw.Region() method: https://ninjatrader.com/support/help...raw_region.htm
                        Brandon H.NinjaTrader Customer Service

                        Comment


                          #13
                          Hi Brandon, thanks for the reply... I did wind up seeing this error, which I fixed, but the region still will not shade. My next attempt will be using datetime instead of barnumber. If I figure it out, I'll repost but if you can think of another plan, please let me know. thnaks

                          Comment


                            #14
                            Maybe this code can be useful. Each intersection is a new "Region".
                            The problem is that the intersections are not perfect. As far as I know, I'm afraid you have to override the "OnRender" method (advanced programming) to get perfect rendering at intersections.
                            Attached Files

                            Comment


                              #15
                              @cis71, thank you for the help! I did not know that I needed to have the DrawOnPricePanel = false; for the Draw.Region to work correctly! You have been extremely helpful and I really appreciate it!!

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by burtoninlondon, Today, 12:38 AM
                              0 responses
                              5 views
                              0 likes
                              Last Post burtoninlondon  
                              Started by AaronKoRn, Yesterday, 09:49 PM
                              0 responses
                              12 views
                              0 likes
                              Last Post AaronKoRn  
                              Started by carnitron, Yesterday, 08:42 PM
                              0 responses
                              11 views
                              0 likes
                              Last Post carnitron  
                              Started by strategist007, Yesterday, 07:51 PM
                              0 responses
                              13 views
                              0 likes
                              Last Post strategist007  
                              Started by StockTrader88, 03-06-2021, 08:58 AM
                              44 responses
                              3,982 views
                              3 likes
                              Last Post jhudas88  
                              Working...
                              X