Ultimate C% Oscillator (Update Feb.17.2025 for 2.0.0.50-RC.419)

This indicator was built by Ali Casey from StatOasis. Ali applied Larry Williams' Ultimate Oscillator concepts to his Casey C% indicator, creating a brand-new oscillator called Ultimate C%.

The indicator is similar to the RSI and is primarily used for mean reversion strategies on indices.

Video for more information:

https://www.youtube.com/watch?v=hQUKA9UvwoQ

namespace CC.Indicators.UltimateC;

public sealed partial class UltimateC_Oscillator : Indicator
{
	[Parameter("Lookback"), NumericRange(1)]
	public int Lookback { get; set; } = 3;

	[Parameter("Factor"), NumericRange(1)]
	public int Factor { get; set; } = 2;

	[Parameter("SmoothLookback"), NumericRange(1)]
	public int SmoothLookback { get; set; } = 3;

	[Plot("Result")]
	public PlotSeries Result { get; set; } = new(Color.Blue);

	[Plot("Overbought Level")]
	public PlotLevel OverboughtLevel { get; set; } = new(75, Color.Red, LineStyle.Dash, 1);

	[Plot("Oversold Level")]
	public PlotLevel OversoldLevel { get; set; } = new(25, Color.Green, LineStyle.Dash, 1);

	private int _barsRequiredToCalculate;
	private DataSeries _ultimateC = null!;
	private DataSeries _roc = null!;
	public UltimateC_Oscillator()
	{
		Name = "Ultimate C% Oscillator";
		ShortName = "CC.UCO";
		ScalePrecision = 1;
		IsOverlay = false;
        IsPercentage = true;
	}

	protected override void Initialize()
	{
		_ultimateC = new DataSeries();
		_roc = new DataSeries();
		_barsRequiredToCalculate = Math.Max(Lookback, SmoothLookback) * Factor * Factor;
	}

	protected override void Calculate(int index)
	{
		if (index <= _barsRequiredToCalculate)
		{
			Result[index] = 0;
			return;
		}
		
		double caseyCShort = 0;
		double caseyCMed = 0;
		double caseyCLong = 0;
		
		
		_roc[index] = (Bars[index].Close - Bars[index - 1].Close) / Bars[index - 1].Close * 100;
		
		double highestShort = HighestT(index, Lookback);
		double lowestShort = LowestT(index, Lookback);
		if ((highestShort - lowestShort) != 0) caseyCShort = (_roc[index] - lowestShort) / (highestShort - lowestShort) * 100;

		double highestMed = HighestT(index, Lookback * Factor);
		double lowestMed = LowestT(index, Lookback * Factor);	
		if ((highestMed - lowestMed) != 0) caseyCMed = (_roc[index] - lowestMed)/(highestMed - lowestMed) * 100;
	
		double highestLong = HighestT(index, Lookback * Factor * Factor );
		double lowestLong = LowestT(index, Lookback * Factor * Factor);	
		if ((highestLong - lowestLong) != 0) caseyCLong = (_roc[index] - lowestLong)/(highestLong - lowestLong) * 100;
		
		_ultimateC[index] = ((caseyCShort * Factor * Factor) + (caseyCMed * Factor) + caseyCLong) /
		            ((Factor * Factor) + Factor + 1);
		
		Result[index] = SSMA(index,SmoothLookback);
		
	}

	
	private double SMA(int index,int period)
	{
		var per = Math.Min(period, index + 1);

		var sum = 0.0;

		for (var i = 0; i < per; i++)
		{
			sum += _ultimateC[index - i];
		}
		return(sum / per);
	}
	
	private double SSMA(int index,int period)
	{
		var per = Math.Min(period, index + 1);

		if (index == per - 1)
		{
			var sum = 0.0;

			for (var i = 0; i < per; i++)
			{
				sum += _ultimateC[index - i];
			}

			return (sum / per);
		}
		else
		{
			return (Result[index - 1] * (per - 1) + _ultimateC[index]) / per;
		}
	}
	
	private double HighestT(int index, int period)
	{
		double highest = double.MinValue;
		int per = Math.Min(period, index + 1);

		for (int i = 0; i < per; i++)
		{

			if (_roc[index-i] > highest) highest = _roc[index-i];
		}

		return highest;
	}
	
	private double LowestT(int index, int period)
	{
		double lowest = double.MaxValue;
		int per = Math.Min(period, index + 1);

		for (int i = 0; i < per; i++)
		{

			if (_roc[index-i] < lowest) lowest = _roc[index-i];
		}

		return lowest;
	}

}

CC.Indicators.UltimateC.zip
110.21KB
3
11 replies