using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
#endregion
// This namespace holds Indicators in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Indicators
{
public class VolumeSpikeNT8V3 : Indicator
{
// State variables for tracking spikes and price movements
private bool isLongSpikeDetected = false;
private bool isShortSpikeDetected = false;
private double spikeLongPrice = 0.0;
private double spikeShortPrice = 0.0;
private int spikeLongBar = 0;
private int spikeShortBar = 0;
// Public boolean to track volume spike status
private bool VolumeSpikeIsTrue = false;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = @"VolumeSpikeNT8 detects significant volume spikes based on price action and a user-defined volume threshold. It triggers signals only when the volume spike is followed by a substantial price movement in the same direction.";
Name = "VolumeSpikeNT8V3";
Calculate = Calculate.OnBarClose;
IsOverlay = true;
DisplayInDataBox = true;
DrawOnPricePanel = true;
DrawHorizontalGridLines = true;
DrawVerticalGridLines = true;
PaintPriceMarkers = true;
ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right;
IsSuspendedWhileInactive = false;
Period = 5; // Look-back period for historical volume comparison
VolumeThreshold = 5000; // Absolute volume threshold for spike detection
PriceMoveThreshold = 100; // Minimum price movement after spike
MaxBarsToCheck = 5; // Maximum bars to wait for price movement confirmation
LongPriceMoveThreshold = 100; // Optional: separate threshold for long
ShortPriceMoveThreshold = 100; // Optional: separate threshold for short
}
else if (State == State.Configure)
{
// Additional configuration logic (if any)
}
}
protected override void OnBarUpdate()
{
// Ensure there are enough bars to perform calculations
if (CurrentBar > Period)
{
// Reset VolumeSpikeIsTrue to false at the start of each bar update
VolumeSpikeIsTrue = false;
// Check for new volume spike conditions only if no active spike is being tracked
if (!isLongSpikeDetected || !isShortSpikeDetected)
{
// LONG SIGNAL CONDITIONS
if (
(Low[0] <= Low[1]) && (Low[0] <= Low[2]) &&
(Close[0] >= Close[1]) && (Close[0] >= Open[0]) &&
(Close[0] - Open[0] < (High[0] - Low[0])) &&
(Volume[1] > MAX(Volume, Period)[2]) &&
(Volume[1] > VolumeThreshold)
)
{
isLongSpikeDetected = true;
spikeLongPrice = Close[1];
spikeLongBar = CurrentBar - 1;
// Draw a marker for the spike
Draw.Dot(this, "LongSpikeDot" + CurrentBar, false, 1, Low[1] - TickSize, Brushes.Green);
// Trigger an alert (optional)
Alert("LongSpikeAlert" + CurrentBar, Priority.High, "Long Volume Spike Detected", "Alert1.wav", 0, Brushes.Transparent, Brushes.Transparent);
}
// SHORT SIGNAL CONDITIONS
if (
(High[0] >= High[1]) && (High[0] >= High[2]) &&
(Close[0] <= Close[1]) && (Close[0] <= Open[0]) &&
((Open[0] - Close[0]) < (High[0] - Low[0])) &&
(Volume[1] > MAX(Volume, Period)[2]) &&
(Volume[1] > VolumeThreshold)
)
{
isShortSpikeDetected = true;
spikeShortPrice = Close[1];
spikeShortBar = CurrentBar - 1; // Draw a marker for the spike
Draw.Dot(this, "ShortSpikeDot" + CurrentBar, false, 1, High[1] + TickSize, Brushes.Red);
// Trigger an alert (optional)
Alert("ShortSpikeAlert" + CurrentBar, Priority.High, "Short Volume Spike Detected", "Alert2.wav", 0, Brushes.Transparent, Brushes.Transparent);
}
}
// Monitor price movement for Long Spike
if (isLongSpikeDetected)
{
int barsSinceSpike = CurrentBar - spikeLongBar;
if (barsSinceSpike <= MaxBarsToCheck)
{
// Check if price has moved long by threshold
if (Close[0] >= spikeLongPrice + LongPriceMoveThreshold)
{
// Trigger Long Signal
BackBrush = Brushes.Green;
// Draw a label for confirmation
Draw.Text(this, "LongSignal" + CurrentBar, "Long Move", 0, spikeLongPrice + LongPriceMoveThreshold + TickSize, Brushes.Green);
// Trigger an alert (optional)
Alert("LongMoveAlert" + CurrentBar, Priority.High, "Price moved long after volume spike", "Alert3.wav", 0, Brushes.Transparent, Brushes.Transparent);
// Reset the spike tracking
VolumeSpikeIsTrue = true; // Set to true on trigger
isLongSpikeDetected = false;
}
}
else
{
// Reset if price movement condition not met within MaxBarsToCheck
isLongSpikeDetected = false;
}
}
// Monitor price movement for Short Spike
if (isShortSpikeDetected)
{
int barsSinceSpike = CurrentBar - spikeShortBar;
if (barsSinceSpike <= MaxBarsToCheck)
{
// Check if price has moved short by threshold
if (Close[0] <= spikeShortPrice - ShortPriceMoveThreshold)
{
// Trigger Short Signal
BackBrush = Brushes.Red;
// Draw a label for confirmation
Draw.Text(this, "ShortSignal" + CurrentBar, "Short Move", 0, spikeShortPrice - ShortPriceMoveThreshold - TickSize, Brushes.Red);
// Trigger an alert (optional)
Alert("ShortMoveAlert" + CurrentBar, Priority.High, "Price moved short after volume spike", "Alert4.wav", 0, Brushes.Transparent, Brushes.Transparent);
// Reset the spike tracking
VolumeSpikeIsTrue = true; // Set to true on trigger
isShortSpikeDetected = false;
}
}
else
{
// Reset if price movement condition not met within MaxBarsToCheck
isShortSpikeDetected = false;
}
}
}
}
region Properties
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name="Period", Description="Look-back period for historical volume comparison.", Order=1, GroupName="Parameters")]
public int Period { get; set; }
[NinjaScriptProperty]
[Range(1, double.MaxValue)]
[Display(Name="Volume Threshold", Description="Minimum volume required to trigger a spike signal.", Order=2, GroupName="Parameters")]
public double VolumeThreshold { get; set; }
[NinjaScriptProperty]
[Range(1, double.MaxValue)]
[Display(Name="Price Move Threshold", Description="Minimum price movement (in points) required after a volume spike to confirm the signal.", Order=3, GroupName="Parameters")]
public double PriceMoveThreshold { get; set; }
[NinjaScriptProperty]
[Range(1, int.MaxValue)]
[Display(Name="Max Bars to Check", Description="Maximum number of bars to wait after a volume spike for price movement confirmation.", Order=4, GroupName="Parameters")]
public int MaxBarsToCheck { get; set; }
[NinjaScriptProperty]
[Range(1, double.MaxValue)]
[Display(Name="Long Price Move Threshold", Description="Minimum price movement (in points) required after a bullish volume spike to confirm the signal.", Order=5, GroupName="Parameters")]
public double LongPriceMoveThreshold { get; set; }
[NinjaScriptProperty]
[Range(1, double.MaxValue)]
[Display(Name="Short Price Move Threshold", Description="Minimum price movement (in points) required after a bearish volume spike to confirm the signal.", Order=6, GroupName="Parameters")]
public double ShortPriceMoveThreshold { get; set; }
// Make VolumeSpikeIsTrue accessible to other scripts
[Browsable(false)]
[XmlIgnore]
public bool volumeSpikeIsTrue
{
get { return VolumeSpikeIsTrue; }
}
#endregion
}
}
region NinjaScript generated code. Neither change nor remove.
namespace NinjaTrader.NinjaScript.Indicators
{
public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
{
private VolumeSpikeNT8V3[] cacheVolumeSpikeNT8V3;
public VolumeSpikeNT8V3 VolumeSpikeNT8V3(int period, double volumeThreshold, double priceMoveThreshold, int maxBarsToCheck, double longPriceMoveThreshold, double shortPriceMoveThreshold)
{
return VolumeSpikeNT8V3(Input, period, volumeThreshold, priceMoveThreshold, maxBarsToCheck, longPriceMoveThreshold, shortPriceMoveThreshold);
}
public VolumeSpikeNT8V3 VolumeSpikeNT8V3(ISeries<double> input, int period, double volumeThreshold, double priceMoveThreshold, int maxBarsToCheck, double longPriceMoveThreshold, double shortPriceMoveThreshold)
{
if (cacheVolumeSpikeNT8V3 != null)
for (int idx = 0; idx < cacheVolumeSpikeNT8V3.Length; idx++)
if (cacheVolumeSpikeNT8V3[idx] != null && cacheVolumeSpikeNT8V3[idx].Period == period && cacheVolumeSpikeNT8V3[idx].VolumeThreshold == volumeThreshold && cacheVolumeSpikeNT8V3[idx].PriceMoveThreshold == priceMoveThreshold && cacheVolumeSpikeNT8V3[idx].MaxBarsToCheck == maxBarsToCheck && cacheVolumeSpikeNT8V3[idx].LongPriceMoveThreshold == longPriceMoveThreshold && cacheVolumeSpikeNT8V3[idx].ShortPriceMoveThreshold == shortPriceMoveThreshold && cacheVolumeSpikeNT8V3[idx].EqualsInput(input))
return cacheVolumeSpikeNT8V3[idx];
return CacheIndicator<VolumeSpikeNT8V3>(new VolumeSpikeNT8V3(){ Period = period, VolumeThreshold = volumeThreshold, PriceMoveThreshold = priceMoveThreshold, MaxBarsToCheck = maxBarsToCheck, LongPriceMoveThreshold = longPriceMoveThreshold, ShortPriceMoveThreshold = shortPriceMoveThreshold }, input, ref cacheVolumeSpikeNT8V3);
}
}
}
: cant get the visuals to show up on the chart when i have this AddChartIndicator() so i could use some help on the proper way to get my indicator to show up in the strategy chart: i works great when just loaded on a chart as a indicator
thanks in advance

Comment