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