Login Page - Create Account

Support Board


Date/Time: Tue, 26 Aug 2025 10:07:44 +0000



Post From: Value Area High/Low

[2025-07-24 11:49:37]
User781731 - Posts: 70
Ok I figured out that you can use the setting "Draw Developing Value Area Lines" to achieve what I'm after.

I also coded my own version. For the most part, the value area high and low closely mirrors the value area identified by the "Volume Value Area Lines" study. But I'm wondering if anyone has any ideas how to get it to match it perfectly? I'd rather not have any discrepancy if possible.

Here is my code:

#include "sierrachart.h"

SCDLLName("A-VAHL")

// Constants
const float VALUE_AREA_PERCENTAGE = 0.70f;

SCSFExport scsf_A_VAHL(SCStudyInterfaceRef sc)
{
SCSubgraphRef VAH = sc.Subgraph[0];
SCSubgraphRef VAL = sc.Subgraph[1];

if (sc.SetDefaults)
{
sc.GraphName = "A-VAHL";
sc.AutoLoop = 1;
sc.GraphRegion = 0;

VAH.Name = "Value Area High";
VAH.DrawStyle = DRAWSTYLE_STAIR_STEP;
VAH.PrimaryColor = RGB(40, 164, 40);
VAH.LineWidth = 1;

VAL.Name = "Value Area Low";
VAL.DrawStyle = DRAWSTYLE_STAIR_STEP;
VAL.PrimaryColor = RGB(255, 0, 0);
VAL.LineWidth = 1;

sc.ValueFormat = VALUEFORMAT_INHERITED;
sc.UpdateAlways = 1;
sc.MaintainVolumeAtPriceData = 1; // Ensure VAP data is available

return;
}

static std::map<float, float> volumeMap; // price → cumulative volume
static float totalVolume = 0.0f;
static SCDateTime lastSessionStart = 0;

SCDateTime currentDT = sc.BaseDateTimeIn[sc.Index];
SCDateTime sessionStart = sc.GetTradingDayStartDateTimeOfBar(sc.BaseDateTimeIn[sc.Index]);

// Reset at session start
if (sessionStart != lastSessionStart)
{
volumeMap.clear();
totalVolume = 0.0f;
lastSessionStart = sessionStart;
}

// Skip if before session start time
if (currentDT < sessionStart)
return;

// Aggregate volume at price
int numPrices = sc.VolumeAtPriceForBars->GetSizeAtBarIndex(sc.Index);
const s_VolumeAtPriceV2* pVAP = nullptr;
for (int i = 0; i < numPrices; ++i)
{
if (!sc.VolumeAtPriceForBars->GetVAPElementAtIndex(sc.Index, i, &pVAP) || pVAP == nullptr)
continue;
float price = pVAP->PriceInTicks * sc.TickSize;
float vol = static_cast<float>(pVAP->Volume);

volumeMap[price] += vol;
totalVolume += vol;
}

// Convert map to sorted vector
std::vector<std::pair<float, float>> sortedV;
for (const auto &entry : volumeMap)
sortedV.emplace_back(entry.first, entry.second);

std::sort(sortedV.begin(), sortedV.end());

// Calculate Value Area
float targetVol = totalVolume * VALUE_AREA_PERCENTAGE;
float bestVAH = 0, bestVAL = 0, bestRange = FLT_MAX;

for (size_t i = 0; i < sortedV.size(); ++i)
{
float cumVol = sortedV[i].second;
float low = sortedV[i].first;
float high = low;

for (size_t j = i + 1; j < sortedV.size(); ++j)
{
cumVol += sortedV[j].second;
high = sortedV[j].first;

if (cumVol >= targetVol)
break;
}

if (cumVol >= targetVol && (high - low) < bestRange)
{
bestRange = high - low;
bestVAL = low;
bestVAH = high;
}
}

VAH[sc.Index] = bestVAH;
VAL[sc.Index] = bestVAL;
}