Login Page - Create Account

Support Board


Date/Time: Thu, 03 Jul 2025 20:20:47 +0000



Post From: Jma custom study for Sierra Chart

[2025-06-15 17:21:03]
User520975 - Posts: 6
Could you add this to your custom studies?

#include "sierrachart.h"

SCDLLName("Jurik Moving Average (JMA)")

SCStudyInterfaceRef g_ChartInterface; // Global reference to access chart data

SCFloatArrayRef JMA = sc.Subgraph[0]; // Main JMA line
SCFloatArrayRef UpperBand = sc.Subgraph[1]; // Upper volatility band
SCFloatArrayRef LowerBand = sc.Subgraph[2]; // Lower volatility band
SCFloatArrayRef VoltyArray = sc.PersistVars->Floats[0]; // Persistent array for volatility (size 11)
SCFloatArrayRef VSum = sc.PersistVars->Floats[11]; // Persistent vSum
SCFloatArrayRef AvgVolty = sc.PersistVars->Floats[12]; // Persistent avgVolty
SCFloatArrayRef Det0 = sc.PersistVars->Floats[13]; // Persistent det0
SCFloatArrayRef Det1 = sc.PersistVars->Floats[14]; // Persistent det1
SCFloatArrayRef MA1 = sc.PersistVars->Floats[15]; // Persistent ma1
SCFloatArrayRef PrevJMA = sc.PersistVars->Floats[16]; // Persistent previous JMA

SCInputRef Period = sc.Input[0];
SCInputRef Phase = sc.Input[1];
SCInputRef Factor = sc.Input[2];
SCInputRef Source = sc.Input[3];

SCSubgraphRef JMA_Subgraph = sc.Subgraph[0];
SCSubgraphRef UpperBand_Subgraph = sc.Subgraph[1];
SCSubgraphRef LowerBand_Subgraph = sc.Subgraph[2];

void InitializeVoltyArray(SCStudyInterfaceRef sc, int index)
{
for (int i = 0; i < 11; i++)
VoltyArray[index * 11 + i] = 0.0f;
}

SCSFExport scsf_JurikMovingAverage(SCStudyInterfaceRef sc)
{
g_ChartInterface = sc; // Store chart interface for global access

if (sc.SetDefaults)
{
sc.GraphName = "Jurik Moving Average (JMA)";
sc.GraphRegion = 0;
sc.AutoLoop = 1;

// Subgraphs
JMA_Subgraph.Name = "JMA";
JMA_Subgraph.DrawStyle = DRAWSTYLE_LINE;
JMA_Subgraph.PrimaryColor = RGB(255, 255, 0); // Yellow
JMA_Subgraph.LineWidth = 2;

UpperBand_Subgraph.Name = "Jurik Upper Band";
UpperBand_Subgraph.DrawStyle = DRAWSTYLE_LINE;
UpperBand_Subgraph.PrimaryColor = RGB(128, 128, 128); // Gray
UpperBand_Subgraph.LineWidth = 1;

LowerBand_Subgraph.Name = "Jurik Lower Band";
LowerBand_Subgraph.DrawStyle = DRAWSTYLE_LINE;
LowerBand_Subgraph.PrimaryColor = RGB(128, 128, 128); // Gray
LowerBand_Subgraph.LineWidth = 1;

// Inputs
Period.Name = "Period";
Period.SetInt(14);
Period.SetIntLimits(1, MAX_STUDY_LENGTH);

Phase.Name = "Phase";
Phase.SetInt(0);
Phase.SetIntLimits(-100, 100);

Factor.Name = "Power";
Factor.SetFloat(0.45f);
Factor.SetFloatLimits(0.1f, 1.0f);

Source.Name = "Source";
Source.SetInputDataIndex(SC_LAST);

// Allocate persistent storage for VoltyArray (11 elements per bar)
sc.PersistVars->AllocateFloatArray(17); // 11 for VoltyArray, 1 each for vSum, avgVolty, det0, det1, ma1, prevJMA

sc.AddFillAreaToGraph(UpperBand_Subgraph, LowerBand_Subgraph, RGB(128, 128, 128), 70, "Jurik Volatility Zone");

return;
}

// Get input values
int period = Period.GetInt();
float phase = Phase.GetInt();
float factor = Factor.GetFloat();
SCFloatArray source = sc.BaseData[Source.GetInputDataIndex()];

// Pre-calculate constants
float phase_value = min(max((phase * 0.01f) + 1.5f, 0.5f), 2.5f);
float beta = factor * (period - 1) / ((factor * (period - 1)) + 2);
float len1 = max((log(sqrt(0.5f * (period - 1))) / log(2.0f)) + 2.0f, 0.0f);
float pow1 = max(len1 - 2.0f, 0.5f);
float len2 = sqrt(0.5f * (period - 1)) * len1;
float pow1Reciprocal = 1.0f / pow1;
float avgVoltyAlpha = 2.0f / (max(4.0f * period, 65) + 1.0f);
float div = 1.0f / (10.0f + 10.0f * (min(max(period - 10, 0), 100) / 100.0f));

int index = sc.Index;

// Initialize persistent variables at the first bar
if (index == 0)
{
UpperBand[index] = source[index];
LowerBand[index] = source[index];
MA1[index] = source[index];
JMA[index] = source[index];
VSum[index] = 0.0f;
Det0[index] = 0.0f;
Det1[index] = 0.0f;
AvgVolty[index] = 0.0f;
PrevJMA[index] = source[index];
InitializeVoltyArray(sc, index);
}
else
{
// Copy previous values
UpperBand[index] = UpperBand[index - 1];
LowerBand[index] = LowerBand[index - 1];
MA1[index] = MA1[index - 1];
JMA[index] = JMA[index - 1];
VSum[index] = VSum[index - 1];
Det0[index] = Det0[index - 1];
Det1[index] = Det1[index - 1];
AvgVolty[index] = AvgVolty[index - 1];
PrevJMA[index] = JMA[index - 1];

// Copy VoltyArray from previous bar
for (int i = 0; i < 11; i++)
VoltyArray[index * 11 + i] = VoltyArray[(index - 1) * 11 + i];
}

// Calculate volatility
float del1 = source[index] - UpperBand[index];
float del2 = source[index] - LowerBand[index];
float volty = (fabs(del1) == fabs(del2)) ? 0.0f : max(fabs(del1), fabs(del2));

// Update VoltyArray (circular buffer)
for (int i = 10; i > 0; i--)
VoltyArray[index * 11 + i] = VoltyArray[index * 11 + (i - 1)];
VoltyArray[index * 11 + 0] = volty;

// Calculate vSum
VSum[index] = VSum[index] + (volty - VoltyArray[index * 11 + 10]) * div;

// Calculate avgVolty
if (index == 0)
AvgVolty[index] = VSum[index];
else
AvgVolty[index] = AvgVolty[index - 1] + avgVoltyAlpha * (VSum[index] - AvgVolty[index - 1]);

// Calculate relative volatility
float rvolty = min(max(AvgVolty[index] > 0 ? volty / AvgVolty[index] : 1.0f, 1.0f), pow(len1, pow1Reciprocal));

// Calculate adaptive parameters
float pow2 = pow(rvolty, pow1);
float Kv = pow(len2 / (len2 + 1), sqrt(pow2));

// Update volatility bands
UpperBand[index] = (del1 > 0) ? source[index] : source[index] - Kv * del1;
LowerBand[index] = (del2 < 0) ? source[index] : source[index] - Kv * del2;

// Calculate dynamic factor alpha
float alpha = pow(beta, pow2);
float alphaSquared = alpha * alpha;
float oneMinusAlpha = 1.0f - alpha;
float oneMinusAlphaSquared = oneMinusAlpha * oneMinusAlpha;

// 1st stage - preliminary smoothing by adaptive EMA
MA1[index] = source[index] + (alpha * (MA1[index - 1] - source[index]));

// 2nd stage - preliminary smoothing by Kalman filter
Det0[index] = (source[index] - MA1[index]) * (1 - beta) + beta * Det0[index - 1];
float ma2 = MA1[index] + (phase_value * Det0[index]);

// 3rd stage - final smoothing by unique Jurik adaptive filter
Det1[index] = ((ma2 - PrevJMA[index]) * oneMinusAlphaSquared) + (alphaSquared * Det1[index - 1]);
JMA[index] = PrevJMA[index] + Det1[index];

// Update PrevJMA for next iteration
PrevJMA[index] = JMA[index];
}