# Cycle Swing Indicator

An accurate, zero-lag Ultra-Smooth Momentum Indicator with less noise and sharp turns. The CSI removes noise and reveals true cycle swings.

# Adaptive Ultra-Smooth Momentum Indicator

Popular technical indicators often react too slowly to changes and deliver shaky signal lines. If they are smoothed, the lag becomes greater and greater, rendering the signals useless for real-time analysis.

When the lag is eliminated and the smoothed, clear reversal points are restored, new options for technical analysis arise. The Cycle-Swing Indicator is fast, clear and smooth. You get better timing, greater accuracy, and better signals.

Many systems include momentum as an indicator. Until now, however, momentum signals have been extremely jittery, resulting in whipsaw trades. In contrast, the adaptive cycle swing generates an ultra-smooth swing without adding lag to the original signal.

The Cycle Swing Indicator "CSI" provides an optimized "momentum" oscillator based on the current dominant cycle by considering the dominant cycle swing instead of the raw source momentum. Offering the following improvements:

- Smoothness
- Zero delay
- Sharpness at turning points
- Robust and adaptable to market conditions
- Accurate deviation detection

The following common problems with standard indicators are solved by this indicator:

First, normal indicators introduce a lot of false signals due to their noisy signal line. Second, to compensate for the noise, one would normally try to add some smoothing. But this only results in adding more delay to the indicator, which makes it almost useless. Third, standard indicators require a length adjustment to derive reliable signals. However, you never know how to set the right length.

All three problems described above are solved by the developed adaptive cyclic algorithm.

[![Screenshot_csi.jpg](https://docs.cycle.tools/uploads/images/gallery/2020-12/scaled-1680-/screenshot-csi.jpg)](https://docs.cycle.tools/uploads/images/gallery/2020-12/screenshot-csi.jpg)

# Source Code

This chapter provides access to source code library for the Cycle Swing Indicator for TradeStation, NinjaTrader, MetaTrader and TradingView. Requires a valid account and login.

# MetaTrader MQ4 - CSI

```C#
//+------------------------------------------------------------------+
//|                                               WTT_CycleSwing.mq4 |
//|                                Copyright 2014- 2017, WhenToTrade |
//|                                      https://www.whentotrade.com |
//|             Book reference: https://www.amazon.com/dp/1974658244 |
//|               License: Attribution 4.0 International (CC BY 4.0) |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014-2017, WhenToTrade"
#property link      "https://www.whentotrade.com"
#property description "WhenToTrade Cycle Swing Indicator (CSI)"
#property description "The CSI is an ultra smooth momentum indicator based on dominant cycles acceleration"
#property description "See book reference: 'Decoding The Hidden Market Rhyhtm - Part 1'"
#property version   "1.00"
#property strict
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot CSI
#property indicator_label1  "CSI"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- indicator buffers
double CSIBuffer[];
double sourceValues[50];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {

//--- indicator buffers mapping
   SetIndexBuffer(0,CSIBuffer);

//name and label
   //MQL4:
   //IndicatorShortName("WTT CSI");
   //MQL5:
   IndicatorSetString(INDICATOR_SHORTNAME,"WTT CSI");
   
   //MQL4:
   // SetIndexLabel(0,"CSI");
   //MQL5:
   PlotIndexSetString(0,PLOT_LABEL,"CSI");

//Set the starting point of CSI
   //MQL4:
   //SetIndexDrawBegin(0,250);
   //MQL5:
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,250);

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

   if(rates_total<=200) return(0);

   //--- start variable for calculation of bars
   int start_index=prev_calculated;

   //--- work at the last bar if the indicator values have already been calculated at the previous tick
   if(prev_calculated>0)
      start_index--;

   //--- define indexing direction in arrays
   bool as_series_closes=ArrayGetAsSeries(close);
   bool as_series_buffer=ArrayGetAsSeries(CSIBuffer);
      
   //--- replace indexing direction with direct one if necessary
   if(as_series_closes)
      ArraySetAsSeries(close,false);
   if(as_series_buffer)
      ArraySetAsSeries(CSIBuffer,false);
  
   //--- calculate indicator values
   for(int i=start_index;i<rates_total;i++)
     {
      //--- skip
      if(i<=50) 
        {
         CSIBuffer[i]=0;
         continue;
        }

      //--- prepare data
      for(int p=0;p<50;p++)
         sourceValues[p]=close[i-49+p];

      //--- pre-process dominant cycle momentum
      double thrust1=iWTT_CSI_processor(sourceValues,1);
      double thrust2=iWTT_CSI_processor(sourceValues,10);
      
      //--- get the final cycle acceleration (aka swing)
      CSIBuffer[i]=thrust1-thrust2;

     }

   //--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| www.WhenToTrade.com Cycle Swing Pre-Processor                    |
//+------------------------------------------------------------------+
double Cycle1(int i, double waveThrottle, int cycs)
{
   if (i==0) return 1.0+waveThrottle;
   if (i==1) return 1.0+waveThrottle*5.0;
   if (i==(cycs-1)) return 1.0+waveThrottle;
   if (i==(cycs-2)) return 1.0+waveThrottle*5.0;
   
   return 6.0*waveThrottle+1.0;
}

double Cycle2(int i, double waveThrottle, int cycs)
{ 
   if (i==0) return -2.0*waveThrottle;
   if (i==(cycs-1)) return 0.0;
   if (i==(cycs-2)) return -2.0*waveThrottle;
   
   return -4.0*waveThrottle;
}

double Cycle3(int i, double waveThrottle, int cycs)
{
   if (i==(cycs-1)) return 0.0;
   if (i==(cycs-2)) return 0.0;
   
   return waveThrottle;
}

double iWTT_CSI_processor(const double &raw[], int CycleCount)
  {
   double wtt1=0,wtt2=0,wtt3=0,wtt4=0,wtt5=0,_wtt1=0,_wtt2=0,_wtt3=0,_wtt4=0,_wtt5=0;
   double momentum,acceleration,swing; 
   int cycs=50;
   double waveThrottle=160*CycleCount;
   double currentVal;

   for(int i=0;i<cycs;i++)
   {
      swing=Cycle1(i,waveThrottle,cycs)-wtt4*wtt1-_wtt5*_wtt2;
      if(swing==0) break;
      momentum= Cycle2(i,waveThrottle,cycs);
      _wtt1  = wtt1;
      wtt1   = (momentum-wtt4*wtt2)/swing;
      
      acceleration=Cycle3(i,waveThrottle,cycs);
      _wtt2  = wtt2;
      wtt2   = acceleration/swing;

      currentVal=(raw[i]-_wtt3*_wtt5-wtt3*wtt4)/swing;
      _wtt3  = wtt3;
      wtt3   = currentVal;
      wtt4   = momentum-wtt5*_wtt1;
      _wtt5  = wtt5;
      wtt5   = acceleration;
   }

   return currentVal;
  }
//+------------------------------------------------------------------+
```

# TradeStation EasyLanguage - CSI

```C#
//
// Cycle Swing Indicator (CSI)
// Measuring cycle momentum with no lag. 
// An ultra-smooth, sharp and leading momentum indicator.
// 
// Version 1.0, EasyLanguage for TradeStation
//
// Copyright (C) 2017-2020, Lars von Thienen
// www.whentotrade.com
//
// Book: Decoding The Hidden Market Rhythm - Part 1 (2017)
// Chapter 10: Cycle Swing Indicator
// Link: https://www.amazon.com/dp/1974658244
//
// 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. 
//

variables:  
	CSIValue( 0 ),
	thrust1(0), thrust2(0);
	
Arrays: 
	wtt_processed[50](0), cycle1[50](0), cycle2[50](0), cycle3[50](0), sourceValues[50](0) ;
	
method double CSI_PreProcessor(int CycleCount)
variables: int x,
	int p,
	double wtt1, 
	double wtt2, 
	double wtt3, 
	double wtt4, 
	double wtt5, 
	double _wtt1,
	double _wtt2,
	double _wtt3,
	double _wtt4,
	double _wtt5,
	double mom, 
	double acc,
	double swing,
	int cycs,
	double waveThrottle, 
	double ChgRatio;

begin
	waveThrottle=160*CycleCount;
	cycs=50;

	for p=0 to 49 begin
		sourceValues[p]=Close[49-p];
	end;
	
	for x=0 to cycs-1 begin
 	wtt_processed[x]=sourceValues[x];
	end;
	
	If cycs <= 5 then return 0;
	
	cycle1[0]= 1.0+waveThrottle;
	cycle2[0]=-2.0*waveThrottle;
	cycle3[0]=     waveThrottle;
	
	for x=1 to (cycs-2)-1
	begin
		cycle1[x]= 6.0*waveThrottle+1.0;
		cycle2[x]=-4.0*waveThrottle;
		cycle3[x]=     waveThrottle;
	end;
	
	cycle1[1]      = 1.0+waveThrottle*5.0;
	cycle1[cycs-1] = 1.0+waveThrottle;
	cycle1[cycs-2] = 1.0+waveThrottle*5.0;
	cycle2[cycs-2] =-2.0*waveThrottle;
	cycle2[cycs-1] = 0.0;
	cycle3[cycs-2] = 0.0;
	cycle3[cycs-1] = 0.0;
	
	
	for x=0 to cycs-1
	begin
		swing=cycle1[x]-wtt4*wtt1-_wtt5*_wtt2;
		
		if swing=0 then 
		begin
			x=cycs; 
		end
		Else 
		begin
			mom= cycle2[x];
			_wtt1  = wtt1;
			wtt1   = (mom-wtt4*wtt2)/swing;
			cycle2[x]=wtt1;
			
			acc=cycle3[x];
			_wtt2  = wtt2;
			wtt2   = acc/swing;
			cycle3[x]=wtt2;
			
			cycle1[x]=(sourceValues[x]-_wtt3*_wtt5-wtt3*wtt4)/swing;
			_wtt3  = wtt3;
			wtt3   = cycle1[x];
			wtt4   = mom-wtt5*_wtt1;
			_wtt5  = wtt5;
			wtt5   = acc;
		end;
	end;
	
	wtt2 = 0;
	wtt1 = cycle1[cycs-1];
	wtt_processed[cycs-1]=wtt1;
	for x=cycs-2-1 downto 0
	begin
		wtt_processed[x]=cycle1[x]-cycle2[x]*wtt1-cycle3[x]*wtt2;
		wtt2=wtt1;
		wtt1=wtt_processed[x];
	end;
		
	return wtt_processed[49];
end;


thrust1=CSI_PreProcessor(1);
thrust2=CSI_PreProcessor(10);

CSIValue = thrust1-thrust2;

Plot1(CSIValue,"CSI");
```

# NinjaTrader Code - CSI

```C#
// 
// Copyright (C) 2017, WhenToTrade.com
// www.whentotrade.com
// Book: Decoding The Hidden Market Rhythm - Part 1 (2017)
// Chapter 10: Cycle Swing Indicator
// Link: https://www.amazon.com/dp/1974658244
//
// 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. 
//

#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 WTTCycleSwingIndicator : Indicator
	{
		//used arrays for calculation
		double[] sourceValues= new double[50];
		double[] cycle1= new double[50];
		double[] cycle2= new double[50];
		double[] cycle3= new double[50];
		double[] wtt_processed= new double[50];
		
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description				= @"CSI: Smooth and fast momentum indicator based on dominant cycle acceleration. wwww.whentotrade.com";
				Name					= "WTT Cycle Swing Indicator";
				Calculate				= Calculate.OnBarClose;
				IsOverlay				= false;
				DisplayInDataBox			= true;
				DrawOnPricePanel			= true;
				DrawHorizontalGridLines			= true;
				DrawVerticalGridLines			= true;
				PaintPriceMarkers			= true;
				ScaleJustification			= NinjaTrader.Gui.Chart.ScaleJustification.Right;
				IsSuspendedWhileInactive		= true;
				AddPlot(Brushes.Crimson, "CSI");
				AddLine(Brushes.SlateBlue,	0,	NinjaTrader.Custom.Resource.NinjaScriptIndicatorZeroLine);
			}
			else if (State == State.Configure)
			{
			}
			else if (State == State.DataLoaded)
			{
			}
		}

		protected override void OnBarUpdate()
		{
			//Add your custom indicator logic here.
			if (CurrentBar + 1 <= 52) 
			{
				CSI[0]=0;
				return;
			}
			
			 //--- calculate indicator values
			      
			//--- prepare data	
			for(int p=0;p<50;p++)
			   sourceValues[p]=Close[49-p];
	
			 //--- pre-process dominant cycle momentum
			 double thrust1=iWTT_CSI_processor(sourceValues,1);
			 double thrust2=iWTT_CSI_processor(sourceValues,10);

			 //--- get the final cycle acceleration (aka swing)
			 CSI[0]=thrust1-thrust2;
			
		}
		
		//+------------------------------------------------------------------+
		//| www.WhenToTrade.com Cycle Swing Pre-Processor                    |
		//+------------------------------------------------------------------+
	  	double iWTT_CSI_processor(double[] raw, int CycleCount)
	  	{
		   double wtt1=0,wtt2=0,wtt3=0,wtt4=0,wtt5=0,_wtt1=0,_wtt2=0,_wtt3=0,_wtt4=0,_wtt5=0;
		   double momentum,acceleration,swing; 
		   
		   int cycs=50;
		   double waveThrottle=160*CycleCount;

		   for(int i=0;i<cycs;i++) wtt_processed[i]=raw[i];

		   if(cycs <= 5) return 0;

		   cycle1[0]= 1.0+waveThrottle;
		   cycle2[0]=-2.0*waveThrottle;
		   cycle3[0]=     waveThrottle;

		   for(int i=1;i<cycs-2;i++)
		     {
		      cycle1[i]= 6.0*waveThrottle+1.0;
		      cycle2[i]=-4.0*waveThrottle;
		      cycle3[i]=     waveThrottle;
		     }

		   cycle1[1]      = 1.0+waveThrottle*5.0;
		   cycle1[cycs-1] = 1.0+waveThrottle;
		   cycle1[cycs-2] = 1.0+waveThrottle*5.0;
		   cycle2[cycs-2] =-2.0*waveThrottle;
		   cycle2[cycs-1] = 0.0;
		   cycle3[cycs-2] = 0.0;
		   cycle3[cycs-1] = 0.0;

		   for(int i=0;i<cycs;i++)
		     {
		      swing=cycle1[i]-wtt4*wtt1-_wtt5*_wtt2;
		      if(swing==0) break;
		      momentum= cycle2[i];
		      _wtt1  = wtt1;
		      wtt1   = (momentum-wtt4*wtt2)/swing;
		      cycle2[i]=wtt1;

		      acceleration=cycle3[i];
		      _wtt2  = wtt2;
		      wtt2   = acceleration/swing;
		      cycle3[i]=wtt2;

		      cycle1[i]=(raw[i]-_wtt3*_wtt5-wtt3*wtt4)/swing;
		      _wtt3  = wtt3;
		      wtt3   = cycle1[i];
		      wtt4   = momentum-wtt5*_wtt1;
		      _wtt5  = wtt5;
		      wtt5   = acceleration;
		     }

		   wtt2 = 0;
		   wtt1 = cycle1[cycs-1];
		   wtt_processed[cycs-1]=wtt1;
		   for(int i=cycs-2; i>=0; i--)
		     {
		      wtt_processed[i]=cycle1[i]-cycle2[i]*wtt1-cycle3[i]*wtt2;
		      wtt2=wtt1;
		      wtt1=wtt_processed[i];
		     }
		     
		   return wtt_processed[49];

	  	}

		#region Properties

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

	}
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private WTTCycleSwingIndicator[] cacheWTTCycleSwingIndicator;
		public WTTCycleSwingIndicator WTTCycleSwingIndicator()
		{
			return WTTCycleSwingIndicator(Input);
		}

		public WTTCycleSwingIndicator WTTCycleSwingIndicator(ISeries<double> input)
		{
			if (cacheWTTCycleSwingIndicator != null)
				for (int idx = 0; idx < cacheWTTCycleSwingIndicator.Length; idx++)
					if (cacheWTTCycleSwingIndicator[idx] != null &&  cacheWTTCycleSwingIndicator[idx].EqualsInput(input))
						return cacheWTTCycleSwingIndicator[idx];
			return CacheIndicator<WTTCycleSwingIndicator>(new WTTCycleSwingIndicator(), input, ref cacheWTTCycleSwingIndicator);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.WTTCycleSwingIndicator WTTCycleSwingIndicator()
		{
			return indicator.WTTCycleSwingIndicator(Input);
		}

		public Indicators.WTTCycleSwingIndicator WTTCycleSwingIndicator(ISeries<double> input )
		{
			return indicator.WTTCycleSwingIndicator(input);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.WTTCycleSwingIndicator WTTCycleSwingIndicator()
		{
			return indicator.WTTCycleSwingIndicator(Input);
		}

		public Indicators.WTTCycleSwingIndicator WTTCycleSwingIndicator(ISeries<double> input )
		{
			return indicator.WTTCycleSwingIndicator(input);
		}
	}
}

#endregion
```