Code: Select all
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using System;
namespace cAlgo
{
[Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class CommodityMoodPro : Indicator
{
private enum MarketRegime
{
BullishAgreement,
BearishAgreement,
Divergence,
Neutral
}
// --- Parameters: Symbols ---
[Parameter("Compare Asset", DefaultValue = "XAUUSD", Group = "Data Sources")]
public string CompareAsset { get; set; }
// --- Parameters: Periods ---
[Parameter("Correlation Length", DefaultValue = 20, MinValue = 5, Group = "Calculations")]
public int CorrLen { get; set; }
[Parameter("Momentum Length (ROC)", DefaultValue = 10, MinValue = 2, Group = "Calculations")]
public int MomLen { get; set; }
[Parameter("Smooth Correlation (EMA)", DefaultValue = 3, MinValue = 1, Group = "Calculations")]
public int SmoothLen { get; set; }
// --- Parameters: Thresholds ---
[Parameter("High Correlation Level", DefaultValue = 0.6, Step = 0.1, Group = "Thresholds")]
public double HighCorrLevel { get; set; }
[Parameter("Low Correlation Level", DefaultValue = 0.3, Step = 0.1, Group = "Thresholds")]
public double LowCorrLevel { get; set; }
// --- Outputs ---
[Output("Bullish Theme", LineColor = "MediumSeaGreen", PlotType = PlotType.Histogram, Thickness = 4)]
public IndicatorDataSeries BullTheme { get; set; }
[Output("Bearish Theme", LineColor = "Crimson", PlotType = PlotType.Histogram, Thickness = 4)]
public IndicatorDataSeries BearTheme { get; set; }
[Output("Divergent Theme", LineColor = "DimGray", PlotType = PlotType.Histogram, Thickness = 4)]
public IndicatorDataSeries DivTheme { get; set; }
[Output("Raw Correlation", LineColor = "FromArgb(80, 30, 144, 255)", PlotType = PlotType.Line, Thickness = 1)]
public IndicatorDataSeries RawCorrelation { get; set; }
[Output("Smoothed Correlation", LineColor = "DodgerBlue", PlotType = PlotType.Line, Thickness = 2)]
public IndicatorDataSeries SmoothedCorrelation { get; set; }
// --- Internal State ---
private Bars _compareBars;
private ExponentialMovingAverage _emaSmoothing;
private string _errorMessage;
protected override void Initialize()
{
try
{
_compareBars = MarketData.GetBars(TimeFrame, CompareAsset);
}
catch (Exception)
{
_errorMessage = $"ERROR: Cannot load symbol '{CompareAsset}'. Check naming convention.";
PrintError(_errorMessage);
return;
}
_emaSmoothing = Indicators.ExponentialMovingAverage(RawCorrelation, SmoothLen);
// Subwindow structural lines
Chart.DrawHorizontalLine("hline_upper", HighCorrLevel, Color.FromArgb(60, Color.Green), 1, LineStyle.Dots);
Chart.DrawHorizontalLine("hline_lower", -HighCorrLevel, Color.FromArgb(60, Color.Red), 1, LineStyle.Dots);
Chart.DrawHorizontalLine("hline_zero", 0.0, Color.FromArgb(100, Color.Gray), 1, LineStyle.Lines);
}
public override void Calculate(int index)
{
if (!string.IsNullOrEmpty(_errorMessage))
{
Chart.DrawText("ErrorMsg", _errorMessage, index, 0, Color.Red);
return;
}
if (index < Math.Max(CorrLen, MomLen)) return;
int compareIndex = _compareBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
if (compareIndex < Math.Max(CorrLen, MomLen)) return;
// 1. Momentum
double primaryPast = Bars.ClosePrices[index - MomLen];
double comparePast = _compareBars.ClosePrices[compareIndex - MomLen];
if (primaryPast == 0 || comparePast == 0) return; // Prevent division by zero
double rocPrimary = (Bars.ClosePrices[index] - primaryPast) / primaryPast * 100.0;
double rocCompare = (_compareBars.ClosePrices[compareIndex] - comparePast) / comparePast * 100.0;
// 2. Pearson Correlation
double r = CalculatePearsonCorrelation(index, compareIndex, CorrLen);
RawCorrelation[index] = r;
// Wait for EMA to seed properly
if (index < Math.Max(CorrLen, MomLen) + SmoothLen) return;
double smoothedR = _emaSmoothing.Result[index];
SmoothedCorrelation[index] = smoothedR;
// 3. Determine Regime
MarketRegime regime = DetermineRegime(smoothedR, rocPrimary, rocCompare);
// 4. Render Histograms
BullTheme[index] = regime == MarketRegime.BullishAgreement ? smoothedR : double.NaN;
BearTheme[index] = regime == MarketRegime.BearishAgreement ? smoothedR : double.NaN;
DivTheme[index] = regime == MarketRegime.Divergence ? smoothedR : double.NaN;
// 5. Update HUD on the last tick
if (IsLastBar)
{
UpdateHUD(regime, smoothedR, rocPrimary, rocCompare);
}
}
private double CalculatePearsonCorrelation(int primaryIndex, int compareIndex, int length)
{
double sumX = 0, sumY = 0;
for (int k = 0; k < length; k++)
{
sumX += Bars.ClosePrices[primaryIndex - k];
sumY += _compareBars.ClosePrices[compareIndex - k];
}
double meanX = sumX / length;
double meanY = sumY / length;
double cov = 0, varX = 0, varY = 0;
for (int k = 0; k < length; k++)
{
double dx = Bars.ClosePrices[primaryIndex - k] - meanX;
double dy = _compareBars.ClosePrices[compareIndex - k] - meanY;
cov += dx * dy;
varX += dx * dx;
varY += dy * dy;
}
return (varX > 0 && varY > 0) ? cov / Math.Sqrt(varX * varY) : 0;
}
private MarketRegime DetermineRegime(double correlation, double rocPrimary, double rocCompare)
{
bool bullishAgreed = rocPrimary > 0 && rocCompare > 0;
bool bearishAgreed = rocPrimary < 0 && rocCompare < 0;
if (correlation > HighCorrLevel && bullishAgreed)
return MarketRegime.BullishAgreement;
if (correlation > HighCorrLevel && bearishAgreed)
return MarketRegime.BearishAgreement;
if (correlation < LowCorrLevel && (!bullishAgreed && !bearishAgreed))
return MarketRegime.Divergence;
return MarketRegime.Neutral;
}
private void UpdateHUD(MarketRegime regime, double correlation, double rocPrimary, double rocCompare)
{
string hudText = $"Regime: {regime}\n" +
$"Corr: {Math.Round(correlation, 2)}\n" +
$"{SymbolName} ROC: {Math.Round(rocPrimary, 3)}%\n" +
$"{CompareAsset} ROC: {Math.Round(rocCompare, 3)}%";
Color hudColor = regime switch
{
MarketRegime.BullishAgreement => Color.MediumSeaGreen,
MarketRegime.BearishAgreement => Color.Crimson,
MarketRegime.Divergence => Color.DimGray,
_ => Color.DarkGray
};
Chart.DrawText("MoodHUD", hudText, Chart.FirstVisibleBarIndex, Chart.TopY, hudColor);
}
}
}