Support Board
Date/Time: Tue, 26 Aug 2025 08:49:37 +0000
Value Area High/Low
View Count: 186
[2025-07-24 06:28:00] |
User781731 - Posts: 70 |
I'm trying to access the intraday Value Area High and Value Area Low values in a spreadsheet study and/or in a custom study. My spreadsheet study is applied to a 5 minute chart. I have multiple other studies, but let's use VWAP as an example. Every row in the spreadsheet corresponds to a 5 minute bar. In the case of VWAP, I can see its unique value at every 5 minute bar. It reflects the changing value throughout the day. It does not display the final VWAP value in every row throughout the day. The issue I have is Value Area High and Value Area Low (from both Volume Value Area Lines or Volume by Price studies) displays the EOD values across every 5 minute bar in the spreadsheet. But I want to see what the VAH/VAL was at each 5 minute bar; obviously this value is changing throughout the day like VWAP or any other measure. So I want to be able to isolate at a specific 5 minute period, what the VAH and VAL were. How can I access this via the spreadsheet? If that isn't possible, can I access this via ACSIL? That would be acceptable also - as long as I can programmatically access the intraday value. |
[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; } |
[2025-07-24 12:36:50] |
User431178 - Posts: 763 |
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.
The discrepancy is due to the use of a sorted price/volume vector, use POC centric expansion instead. When you convert the volume-at-price map to a sorted vector and then scan for the narrowest price range that contains the target volume: - You lose price-locality logic. - The algorithm no longer respects how volume clusters around the Point of Control (POC). - It treats all starting points equally — even low-volume edge prices — and evaluates all possible subranges. As a result, you may get a value area that's technically correct by volume, but misaligned with where the market actually traded most heavily. |
[2025-07-24 13:17:00] |
User781731 - Posts: 70 |
Thanks so much for your reply! It is exactly the same as the built-in study now. I'm not a programmer and use ChatGPT for this, so I really appreciate it. The only issue is the script appears to be causing the chart to freeze. Do you know why this might be happening and what I can do to fix this issue? #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; return; } // --- Session-persistent storage --- static std::map<float, float> sessionVolumeMap; // price -> cumulative volume for session static float sessionTotalVolume = 0.0f; static SCDateTime lastSessionStart = 0; SCDateTime currentDT = sc.BaseDateTimeIn[sc.Index]; SCDateTime sessionStart = sc.GetTradingDayStartDateTimeOfBar(currentDT); // Reset at new session if (sessionStart != lastSessionStart) { sessionVolumeMap.clear(); sessionTotalVolume = 0.0f; lastSessionStart = sessionStart; } // Aggregate VAP data for all bars in session up to and including current bar sessionVolumeMap.clear(); sessionTotalVolume = 0.0f; for (int i = 0; i <= sc.Index; ++i) { SCDateTime barDT = sc.BaseDateTimeIn[i]; SCDateTime barSessionStart = sc.GetTradingDayStartDateTimeOfBar(barDT); if (barSessionStart != sessionStart) continue; // skip bars from previous sessions int numLevels = sc.VolumeAtPriceForBars->GetSizeAtBarIndex(i); const s_VolumeAtPriceV2* pVAP = nullptr; for (int j = 0; j < numLevels; ++j) { if (!sc.VolumeAtPriceForBars->GetVAPElementAtIndex(i, j, &pVAP) || !pVAP) continue; float price = pVAP->PriceInTicks * sc.TickSize; float vol = static_cast<float>(pVAP->Volume); sessionVolumeMap[price] += vol; sessionTotalVolume += vol; } } if (sessionVolumeMap.empty() || sessionTotalVolume == 0.0f) return; // Build sorted list of (price, volume) std::vector<std::pair<float, float>> sortedV; sortedV.reserve(sessionVolumeMap.size()); for (const auto& e : sessionVolumeMap) sortedV.emplace_back(e.first, e.second); std::sort(sortedV.begin(), sortedV.end(), [](auto &a, auto &b){ return a.first < b.first; }); float targetVol = sessionTotalVolume * VALUE_AREA_PERCENTAGE; // 1) Find POC int pocIndex = 0; float pocVol = sortedV[0].second; for (int i = 1; i < (int)sortedV.size(); ++i) { if (sortedV[i].second > pocVol) { pocVol = sortedV[i].second; pocIndex = i; } } // 2) POC-centric expansion: float cumVol = pocVol; float bestVAL = sortedV[pocIndex].first; float bestVAH = sortedV[pocIndex].first; int left = pocIndex - 1; int right = pocIndex + 1; while (cumVol < targetVol && (left >= 0 || right < (int)sortedV.size())) { float leftVol = (left >= 0 ? sortedV[left].second : 0.0f); float rightVol = (right < (int)sortedV.size() ? sortedV[right].second : 0.0f); // --- equal-volume case: include both simultaneously --- if (left >= 0 && right < (int)sortedV.size() && leftVol == rightVol) { cumVol += leftVol + rightVol; bestVAL = sortedV[left].first; bestVAH = sortedV[right].first; --left; ++right; } // only left side remains else if (right >= (int)sortedV.size()) { cumVol += leftVol; bestVAL = sortedV[left].first; --left; } // only right side remains else if (left < 0) { cumVol += rightVol; bestVAH = sortedV[right].first; ++right; } // pick the side with greater volume else if (leftVol > rightVol) { cumVol += leftVol; bestVAL = sortedV[left].first; --left; } else { cumVol += rightVol; bestVAH = sortedV[right].first; ++right; } } VAH[sc.Index] = bestVAH; VAL[sc.Index] = bestVAL; } Date Time Of Last Edit: 2025-07-24 13:17:33
|
[2025-07-24 13:29:10] |
User781731 - Posts: 70 |
Ok correction; it appears to work perfectly on historical bars but not the most recent bars. See screenshot. My study is the thin lines; the SC "Volume Value Area Lines" study is the thick lines. You can see previously they overlap perfectly but the realtime isn't overlapping. This is the most important for me because it will be used as part of an automated strategy. Edit: it appears this issue only happens when viewing symbols with delayed data. Date Time Of Last Edit: 2025-07-24 13:44:22
|
![]() |
[2025-07-24 17:05:51] |
User431178 - Posts: 763 |
I'm not a programmer and use ChatGPT for this
😁 I was going to mention that it looked like GPT code. The only issue is the script appears to be causing the chart to freeze. Do you know why this might be happening and what I can do to fix this issue?
Iterating from 0 to sc.Index on every call to the function probably has something to do with it. |
To post a message in this thread, you need to log in with your Sierra Chart account: