Skip to main content

Custom Strategy — VWAP/POC

Demonstrates a custom strategy (without Algo.Strategy) that uses StrategyVolumeProfileIndicator with VWAP standard deviation and a running average POC calculation. Uses NinjaTrader EnterLong/EnterShort directly with OnBarCloseHandler logic, suitable for backtesting in Strategy Analyzer.

Source: [INSTALL PATH]/API/Samples/MZpackCustomStrategy0.cs Class: MZpackCustomStrategy0 : MZpackStrategyBase

What It Covers

  • StrategyVolumeProfileIndicator with session profiles and VWAP with standard deviation
  • OnBarCloseHandler strategy logic: SHORT above positive deviation + above average POC; LONG below negative deviation + below average POC
  • Running POC calculation over configurable bars back
  • EnableBacktesting = true for Strategy Analyzer compatibility
  • Profile access via (sessionVPIndicator.Profiles as Model).GetItemByBarIdx(currentBarIdx)

Indicator Setup

sessionVPIndicator = new StrategyVolumeProfileIndicator(this, @"Session Volume Profile")
{
ProfileCreation = ProfileCreation.Session,
ShowProfileType = ProfileType.VP,
Sigma1 = VP_Sigma1,
VWAPMode = VWAPMode.DynamicStdDev1,
ProfileWidthPercentage = 20,
POCMode = LevelMode.Developing,
VAHVALMode = LevelMode.Off,
StackedProfileCreation1 = ProfileCreation.None,
StackedProfileCreation2 = ProfileCreation.None,
StackedProfileCreation3 = ProfileCreation.None
};

Core Strategy Logic

The OnBarCloseHandler computes VWAP deviation bands and a running average POC, then enters short when price closes above the positive deviation and above average POC, or enters long when price closes below the negative deviation and below average POC:

protected void StrategyOnBarCloseHandler(MarketDataEventArgs e, int currentBarIdx)
{
IVolumeProfile profile = (sessionVPIndicator.Profiles as Model)
.GetItemByBarIdx(currentBarIdx) as IVolumeProfile;

if (profile != null)
{
double positiveDeviation1 = profile.VWAP + profile.Deviation * sessionVPIndicator.Sigma1;
double negativeDeviation1 = profile.VWAP - profile.Deviation * sessionVPIndicator.Sigma1;

// Running average POC
if (profile.POCs.Count == 1)
runningPOC = 0;
runningPOC += profile.POCs[currentBarIdx];
if (profile.POCs.Count > Strategy_AveragePOCBarsBack)
runningPOC -= profile.POCs[currentBarIdx - (Strategy_AveragePOCBarsBack - 1)];
double averagePOC = runningPOC / Math.Min(Strategy_AveragePOCBarsBack, profile.POCs.Count);

// Short
if (Close[1] > positiveDeviation1 && averagePOC < Close[1])
{
string signalShort = string.Format("stdDev1:{0:N2} av.POC:{1:N2}",
positiveDeviation1, averagePOC);
SetProfitTarget(signalShort, CalculationMode.Ticks, Strategy_TakeProfitTicks);
SetStopLoss(signalShort, CalculationMode.Ticks, Strategy_StopLossTicks, false);
EnterShort(signalShort);
}
// Long
else if (Close[1] < negativeDeviation1 && averagePOC > Close[1])
{
string signalLong = string.Format("stdDev1:{0:N2} av.POC:{1:N2}",
negativeDeviation1, averagePOC);
SetProfitTarget(signalLong, CalculationMode.Ticks, Strategy_TakeProfitTicks);
SetStopLoss(signalLong, CalculationMode.Ticks, Strategy_StopLossTicks, false);
EnterLong(signalLong);
}
}
}

Configurable Properties

Strategy

PropertyDefaultDescription
Strategy_AveragePOCBarsBack30Bars back for average POC calculation
Strategy_TakeProfitTicks90Take profit ticks
Strategy_StopLossTicks12Stop loss ticks

Volume Profile

PropertyDefaultDescription
VP_ProfileModeVolumeProfile mode
VP_TicksPerLevel1Ticks per level
VP_Sigma11Standard deviation multiplier
VP_VWAPStrokeRed, Dash, 3VWAP line style
VP_Deviation1StrokeGreen1st deviation line style

See Also