Login Page - Create Account

Support Board


Date/Time: Thu, 27 Aug 2026 17:11:00 +0000



Having Volume Profile and Delta Profile side by side.

View Count: 7162

[2026-07-23 13:32:39]
blazemk - Posts: 14
rc_camilo this is GOLD! Nice job
[2026-08-20 20:03:12]
User591238 - Posts: 1
I entered rc_camilo's code into Google Gemini and got it to add a setting to apply the study to session volume profiles as well and not just user drawn profiles. If you do not want this feature you can toggle this setting off so that the study only gets applied to user drawn profiles.

I also tried to add a setting to display volume in bars so i could see the delta in text form but after many attempts I have been unable to get Gemini to do it. If anyone else can figure out how to do it, it would be greatly appreciated.



//=============================================================================
// ROD176 - Delta Profile (left of Volume Profile tool & Daily/Session VBP)
//=============================================================================

#define NOMINMAX
#include "sierrachart.h"
#include <map>
#include <algorithm>
#include <cmath>

SCDLLName("ROD176 - Delta Profile");

struct VPProfileRange
{
SCDateTime BeginDT;
SCDateTime EndDT;
};

void DrawDeltaProfileToChart(HWND WindowHandle, HDC DeviceContext, SCStudyInterfaceRef sc);

SCSFExport scsf_ROD176DeltaProfile(SCStudyInterfaceRef sc)
{
SCInputRef AskColorInput = sc.Input[0];
SCInputRef BidColorInput = sc.Input[1];
SCInputRef TicksCombineInput = sc.Input[2];
SCInputRef EnableSessionProfilesInput = sc.Input[3];

if (sc.SetDefaults)
{
sc.GraphName = "ROD176 - Delta Profile (left of VP & Session VBP)";
sc.StudyDescription =
"ROD176-DeltaProfile\n\n"
"Detects drawn Volume Profiles AND calculates Session/Evening "
"Delta Profiles matching Volume by Price study settings.\n"
"Delta = Ask - Bid per (binned) price level for the exact same time range.\n"
"Drawn using GDI anchored left of Begin bar, extending leftward.";

sc.AutoLoop = 0;
sc.GraphRegion = 0;
sc.UpdateAlways = 1;
sc.MaintainVolumeAtPriceData = 1;

sc.p_GDIFunction = DrawDeltaProfileToChart;

AskColorInput.Name = "Ask Color (Positive Delta)";
AskColorInput.SetColor(RGB(0, 180, 0));

BidColorInput.Name = "Bid Color (Negative Delta)";
BidColorInput.SetColor(RGB(200, 0, 0));

TicksCombineInput.Name = "Number of Ticks to Combine";
TicksCombineInput.SetInt(1);
TicksCombineInput.SetIntLimits(1, 100);

EnableSessionProfilesInput.Name = "Enable Session & Evening Splits (Matches VBP)";
EnableSessionProfilesInput.SetYesNo(1);

return;
}
}

void DrawDeltaProfileToChart(HWND /*WindowHandle*/, HDC /*DeviceContext*/, SCStudyInterfaceRef sc)
{
const COLORREF AskColor = sc.Input[0].GetColor();
const COLORREF BidColor = sc.Input[1].GetColor();
const int CombineTicks = sc.Input[2].GetInt();
const int EnableSessionProfiles = sc.Input[3].GetYesNo();

if (CombineTicks < 1)
return;

std::vector<VPProfileRange> profileRanges;

// -------------------------------------------------------------------
// 1. DETECT USER-DRAWN VOLUME PROFILES
// -------------------------------------------------------------------
int DrawingIndex = 0;
s_UseTool Tool;
Tool.Clear();

while (sc.GetUserDrawnChartDrawing(sc.ChartNumber, DRAWING_VOLUME_PROFILE, Tool, DrawingIndex++))
{
if (Tool.BeginDateTime > 0 && Tool.EndDateTime > Tool.BeginDateTime)
{
profileRanges.push_back({ Tool.BeginDateTime, Tool.EndDateTime });
}
}

// -------------------------------------------------------------------
// 2. DETECT SESSION & EVENING BOUNDARIES (Matches VBP Session Splits)
// -------------------------------------------------------------------
if (EnableSessionProfiles && sc.ArraySize > 0)
{
int currentStartBar = 0;

for (int bar = 1; bar < sc.ArraySize; ++bar)
{
SCDateTime currentDT = sc.BaseDateTimeIn[bar];
SCDateTime prevDT = sc.BaseDateTimeIn[bar - 1];

// Get time-of-day in seconds for current and previous bars
int currentTimeSec = currentDT.GetTimeInSeconds();
int prevTimeSec = prevDT.GetTimeInSeconds();

int dayStartSec = sc.StartTime1; // Main session open
int eveStartSec = sc.StartTime2; // Evening session open

bool isNewDay = sc.IsNewTradingDay(bar);
bool isEveningStart = (eveStartSec > 0 && prevTimeSec < eveStartSec && currentTimeSec >= eveStartSec);
bool isDayStart = (dayStartSec > 0 && prevTimeSec < dayStartSec && currentTimeSec >= dayStartSec);

// Split profile whenever a new day, day session, or evening session starts
if (isNewDay || isEveningStart || isDayStart)
{
if (bar - 1 >= currentStartBar)
{
profileRanges.push_back({ sc.BaseDateTimeIn[currentStartBar], sc.BaseDateTimeIn[bar - 1] });
}
currentStartBar = bar;
}
}

// Add active/current session
if (currentStartBar < sc.ArraySize)
{
profileRanges.push_back({ sc.BaseDateTimeIn[currentStartBar], sc.BaseDateTimeIn[sc.ArraySize - 1] });
}
}

if (profileRanges.empty())
return;

// -------------------------------------------------------------------
// 3. RENDER DELTA PROFILE FOR EACH DETECTED RANGE
// -------------------------------------------------------------------
for (const auto& range : profileRanges)
{
int StartBar = sc.GetContainingIndexForSCDateTime(sc.ChartNumber, range.BeginDT.GetAsDouble());
int EndBar = sc.GetContainingIndexForSCDateTime(sc.ChartNumber, range.EndDT.GetAsDouble());

if (StartBar < 0 || EndBar < StartBar || StartBar >= sc.ArraySize)
continue;

if (EndBar >= sc.ArraySize)
EndBar = sc.ArraySize - 1;

int OriginX = sc.BarIndexToXPixelCoordinate(StartBar);

// Build binned delta map for bars in [StartBar .. EndBar]
std::map<int, double> DeltaBins;

for (int bar = StartBar; bar <= EndBar; ++bar)
{
int numLevels = sc.VolumeAtPriceForBars->GetSizeAtBarIndex(bar);
if (numLevels <= 0)
continue;

for (int lev = 0; lev < numLevels; ++lev)
{
const s_VolumeAtPriceV2* pVAP = nullptr;
if (!sc.VolumeAtPriceForBars->GetVAPElementAtIndex(bar, lev, &pVAP) || pVAP == nullptr)
continue;

int rawTick = pVAP->PriceInTicks;
int binnedTick = (rawTick / CombineTicks) * CombineTicks;

double barDeltaAtLevel = (double)pVAP->AskVolume - (double)pVAP->BidVolume;
DeltaBins[binnedTick] += barDeltaAtLevel;
}
}

if (DeltaBins.empty())
continue;

double maxAbs = 0.0;
for (const auto& kv : DeltaBins)
{
double a = std::abs(kv.second);
if (a > maxAbs) maxAbs = a;
}
if (maxAbs < 0.0001)
continue;

const int MaxProfileWidth = 110;
double scale = (double)MaxProfileWidth / maxAbs;

int region = sc.GraphRegion;
int topClip = (int)sc.StudyRegionTopCoordinate;
int botClip = (int)sc.StudyRegionBottomCoordinate;

for (const auto& kv : DeltaBins)
{
double deltaVal = kv.second;
double ad = std::abs(deltaVal);
if (ad < 0.0001)
continue;

int binnedTick = kv.first;
double centerPrice = (double)binnedTick * sc.TickSize;

double halfBinPrice = (CombineTicks * sc.TickSize) * 0.5;
int yTop = sc.RegionValueToYPixelCoordinate((float)(centerPrice + halfBinPrice), region);
int yBot = sc.RegionValueToYPixelCoordinate((float)(centerPrice - halfBinPrice), region);

if (yTop > yBot) std::swap(yTop, yBot);

if (yTop < topClip) yTop = topClip;
if (yBot > botClip) yBot = botClip;

if (yTop >= yBot)
continue;

int barWidth = 2;
if (ad * scale > 2) barWidth = (int)(ad * scale + 0.5);

int xLeft = OriginX - barWidth;
int xRight = OriginX;

COLORREF col = (deltaVal > 0.0) ? AskColor : BidColor;

n_ACSIL::s_GraphicsBrush gBrush;
gBrush.m_BrushType = n_ACSIL::s_GraphicsBrush::BRUSH_TYPE_SOLID;
gBrush.m_BrushColor.SetRGB(col & 0xFF, (col >> 8) & 0xFF, (col >> 16) & 0xFF);
sc.Graphics.SetBrush(gBrush);

n_ACSIL::s_GraphicsPen gPen;
gPen.m_PenColor.SetRGB(col & 0xFF, (col >> 8) & 0xFF, (col >> 16) & 0xFF);
gPen.m_Width = 1;
gPen.m_PenStyle = n_ACSIL::s_GraphicsPen::e_PenStyle::PEN_STYLE_SOLID;
sc.Graphics.SetPen(gPen);

sc.Graphics.DrawRectangle(xLeft, yTop, xRight, yBot);
}
}
}

To post a message in this thread, you need to log in with your Sierra Chart account:

Login

Login Page - Create Account