NQ Phantom Scalper Pro Strategy
VWAP band breakout strategy with optional volume and trend filters.
Details
- Entry Criteria:
- Long: Price closes above the upper VWAP band with confirming volume.
- Short: Price closes below the lower VWAP band with confirming volume.
- Long/Short: Both
- Exit Criteria:
- Price crosses back through VWAP or the ATR stop is hit.
- Stops: ATR-based
- Default Values:
Band #1 Mult= 1.0Band #2 Mult= 2.0ATR Length= 14ATR Stop Mult= 1.0Volume SMA Period= 20Volume Spike Mult= 1.5Trend EMA Length= 50
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: VWAP, ATR, EMA, SMA
- Stops: Yes
- Complexity: Medium
- Timeframe: Short-term
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// NQ Phantom Scalper Pro strategy based on VWAP bands with optional volume and trend filters.
/// </summary>
public class NqPhantomScalperProStrategy : Strategy
{
private readonly StrategyParam<decimal> _band1Mult;
private readonly StrategyParam<decimal> _atrStopMult;
private readonly StrategyParam<bool> _useTrendFilter;
private readonly StrategyParam<int> _trendLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _stopPrice;
private DateTimeOffset _lastSignal = DateTimeOffset.MinValue;
public decimal Band1Mult
{
get => _band1Mult.Value;
set => _band1Mult.Value = value;
}
public decimal AtrStopMult
{
get => _atrStopMult.Value;
set => _atrStopMult.Value = value;
}
public bool UseTrendFilter
{
get => _useTrendFilter.Value;
set => _useTrendFilter.Value = value;
}
public int TrendLength
{
get => _trendLength.Value;
set => _trendLength.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public NqPhantomScalperProStrategy()
{
_band1Mult = Param(nameof(Band1Mult), 1.0m)
.SetGreaterThanZero();
_atrStopMult = Param(nameof(AtrStopMult), 1.0m)
.SetGreaterThanZero();
_useTrendFilter = Param(nameof(UseTrendFilter), false);
_trendLength = Param(nameof(TrendLength), 50)
.SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_stopPrice = 0m;
_lastSignal = DateTimeOffset.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0m;
_stopPrice = 0m;
_lastSignal = DateTimeOffset.MinValue;
var vwap = new VolumeWeightedMovingAverage();
var atr = new AverageTrueRange { Length = 14 };
var std = new StandardDeviation { Length = 20 };
var trendEma = new ExponentialMovingAverage { Length = TrendLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(vwap, atr, std, trendEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, vwap);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue vwapValue, IIndicatorValue atrValue, IIndicatorValue stdValue, IIndicatorValue trendValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!vwapValue.IsFinal || !vwapValue.IsFormed || !atrValue.IsFormed || !stdValue.IsFormed)
return;
var vwapVal = vwapValue.ToDecimal();
var atr = atrValue.ToDecimal();
var stdVal = stdValue.ToDecimal();
var trendVal = trendValue.ToDecimal();
if (atr <= 0 || stdVal <= 0)
return;
var upper1 = vwapVal + stdVal * Band1Mult;
var lower1 = vwapVal - stdVal * Band1Mult;
var trendOkLong = !UseTrendFilter || candle.ClosePrice > trendVal;
var trendOkShort = !UseTrendFilter || candle.ClosePrice < trendVal;
var cooldown = TimeSpan.FromMinutes(360);
if (candle.OpenTime - _lastSignal < cooldown)
return;
// Exit logic - only on stop loss
if (Position > 0 && _stopPrice > 0 && candle.ClosePrice <= _stopPrice)
{
SellMarket();
_stopPrice = 0m;
_lastSignal = candle.OpenTime;
return;
}
if (Position < 0 && _stopPrice > 0 && candle.ClosePrice >= _stopPrice)
{
BuyMarket();
_stopPrice = 0m;
_lastSignal = candle.OpenTime;
return;
}
// Entry logic
if (Position <= 0 && candle.ClosePrice > upper1 && trendOkLong)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - atr * AtrStopMult * 3;
_lastSignal = candle.OpenTime;
}
else if (Position >= 0 && candle.ClosePrice < lower1 && trendOkShort)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + atr * AtrStopMult * 3;
_lastSignal = candle.OpenTime;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import VolumeWeightedMovingAverage, AverageTrueRange, StandardDeviation, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class nq_phantom_scalper_pro_strategy(Strategy):
def __init__(self):
super(nq_phantom_scalper_pro_strategy, self).__init__()
self._band1_mult = self.Param("Band1Mult", 1.0).SetGreaterThanZero()
self._atr_stop_mult = self.Param("AtrStopMult", 1.0).SetGreaterThanZero()
self._use_trend_filter = self.Param("UseTrendFilter", False)
self._trend_length = self.Param("TrendLength", 50).SetGreaterThanZero()
self._candle_type = self.Param("CandleType", tf(5))
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(nq_phantom_scalper_pro_strategy, self).OnReseted()
self._entry_price = 0
self._stop_price = 0
self._last_signal = None
def OnStarted2(self, time):
super(nq_phantom_scalper_pro_strategy, self).OnStarted2(time)
self._entry_price = 0
self._stop_price = 0
self._last_signal = None
vwap = VolumeWeightedMovingAverage()
atr = AverageTrueRange()
atr.Length = 14
std = StandardDeviation()
std.Length = 20
trend_ema = ExponentialMovingAverage()
trend_ema.Length = self._trend_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(vwap, atr, std, trend_ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, vwap)
self.DrawOwnTrades(area)
def OnProcess(self, candle, vwap_val, atr_val, std_val, trend_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0 or std_val <= 0:
return
upper1 = vwap_val + std_val * self._band1_mult.Value
lower1 = vwap_val - std_val * self._band1_mult.Value
trend_ok_long = not self._use_trend_filter.Value or candle.ClosePrice > trend_val
trend_ok_short = not self._use_trend_filter.Value or candle.ClosePrice < trend_val
cooldown = TimeSpan.FromMinutes(360)
if self._last_signal is not None and (candle.OpenTime - self._last_signal) < cooldown:
return
if self.Position > 0 and self._stop_price > 0 and candle.ClosePrice <= self._stop_price:
self.SellMarket()
self._stop_price = 0
self._last_signal = candle.OpenTime
return
if self.Position < 0 and self._stop_price > 0 and candle.ClosePrice >= self._stop_price:
self.BuyMarket()
self._stop_price = 0
self._last_signal = candle.OpenTime
return
if self.Position <= 0 and candle.ClosePrice > upper1 and trend_ok_long:
self.BuyMarket()
self._entry_price = candle.ClosePrice
self._stop_price = self._entry_price - atr_val * self._atr_stop_mult.Value * 3
self._last_signal = candle.OpenTime
elif self.Position >= 0 and candle.ClosePrice < lower1 and trend_ok_short:
self.SellMarket()
self._entry_price = candle.ClosePrice
self._stop_price = self._entry_price + atr_val * self._atr_stop_mult.Value * 3
self._last_signal = candle.OpenTime
def CreateClone(self):
return nq_phantom_scalper_pro_strategy()