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;
}
}