Vwap Cci Strategy
Implementation of strategy - VWAP + CCI. Buy when price is below VWAP and CCI is below -100 (oversold). Sell when price is above VWAP and CCI is above 100 (overbought).
Testing indicates an average annual return of about 82%. It performs best in the stocks market.
VWAP acts as a value benchmark, and CCI highlights momentum moves away from it. Entries favor strong CCI readings relative to VWAP.
Designed for day traders focusing on VWAP interaction. ATR stops help maintain discipline.
Details
- Entry Criteria:
- Long:
Close < VWAP && CCI < CciOversold - Short:
Close > VWAP && CCI > CciOverbought
- Long:
- Long/Short: Both
- Exit Criteria:
- Price crosses back through VWAP
- Stops: Percent-based using
StopLoss - Default Values:
CciPeriod= 20CciOversold= -100mCciOverbought= 100mStopLoss= new Unit(2, UnitTypes.Percent)CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Mean reversion
- Direction: Both
- Indicators: VWAP, CCI
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
using StockSharp.Algo.Candles;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Implementation of strategy - VWAP + CCI.
/// Buy when price is below VWAP and CCI is below -100 (oversold).
/// Sell when price is above VWAP and CCI is above 100 (overbought).
/// </summary>
public class VwapCciStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciOversold;
private readonly StrategyParam<decimal> _cciOverbought;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<Unit> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private int _cooldown;
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// CCI oversold level.
/// </summary>
public decimal CciOversold
{
get => _cciOversold.Value;
set => _cciOversold.Value = value;
}
/// <summary>
/// CCI overbought level.
/// </summary>
public decimal CciOverbought
{
get => _cciOverbought.Value;
set => _cciOverbought.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop-loss value.
/// </summary>
public Unit StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Candle type used for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="VwapCciStrategy"/>.
/// </summary>
public VwapCciStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Period for Commodity Channel Index", "CCI Parameters");
_cciOversold = Param(nameof(CciOversold), -20m)
.SetDisplay("CCI Oversold", "CCI level to consider market oversold", "CCI Parameters");
_cciOverbought = Param(nameof(CciOverbought), 20m)
.SetDisplay("CCI Overbought", "CCI level to consider market overbought", "CCI Parameters");
_cooldownBars = Param(nameof(CooldownBars), 120)
.SetRange(5, 500)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_stopLoss = Param(nameof(StopLoss), new Unit(2, UnitTypes.Percent))
.SetDisplay("Stop Loss", "Stop loss percent or value", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var vwap = new VolumeWeightedMovingAverage();
var cci = new CommodityChannelIndex { Length = CciPeriod };
// Setup candle subscription
var subscription = SubscribeCandles(CandleType);
// Bind indicators to candles
subscription
.Bind(vwap, cci, ProcessCandle)
.Start();
// Setup chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, vwap);
// Create separate area for CCI
var cciArea = CreateChartArea();
if (cciArea != null)
{
DrawIndicator(cciArea, cci);
}
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal vwapValue, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Current price
var price = candle.ClosePrice;
// Determine if price is above or below VWAP
var isPriceAboveVWAP = price > vwapValue;
LogInfo($"Candle: {candle.OpenTime}, Close: {price}, " +
$"VWAP: {vwapValue}, Price > VWAP: {isPriceAboveVWAP}, " +
$"CCI: {cciValue}");
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Trading rules
var belowVwap = price <= vwapValue * 1.001m;
var aboveVwap = price >= vwapValue * 0.999m;
if (belowVwap && cciValue <= CciOversold && Position == 0)
{
// Buy signal - price below VWAP and CCI oversold
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Buy signal: Price below VWAP and CCI oversold ({cciValue} <= {CciOversold}).");
}
else if (aboveVwap && cciValue >= CciOverbought && Position == 0)
{
// Sell signal - price above VWAP and CCI overbought
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Sell signal: Price above VWAP and CCI overbought ({cciValue} >= {CciOverbought}).");
}
// Exit conditions
else if (isPriceAboveVWAP && Position > 0)
{
// Exit long position when price crosses above VWAP
SellMarket();
_cooldown = CooldownBars;
LogInfo($"Exit long: Price crossed above VWAP. Position: {Position}");
}
else if (!isPriceAboveVWAP && Position < 0)
{
// Exit short position when price crosses below VWAP
BuyMarket();
_cooldown = CooldownBars;
LogInfo($"Exit short: Price crossed below VWAP. Position: {Position}");
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import VolumeWeightedMovingAverage, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class vwap_cci_strategy(Strategy):
"""
Implementation of strategy - VWAP + CCI.
Buy when price is below VWAP and CCI is below -20 (oversold).
Sell when price is above VWAP and CCI is above 20 (overbought).
"""
def __init__(self):
super(vwap_cci_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("CCI Period", "Period for Commodity Channel Index", "CCI Parameters")
self._cci_oversold = self.Param("CciOversold", -20.0) \
.SetDisplay("CCI Oversold", "CCI level to consider market oversold", "CCI Parameters")
self._cci_overbought = self.Param("CciOverbought", 20.0) \
.SetDisplay("CCI Overbought", "CCI level to consider market overbought", "CCI Parameters")
self._cooldown_bars = self.Param("CooldownBars", 120) \
.SetRange(5, 500) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._stop_loss = self.Param("StopLoss", Unit(2, UnitTypes.Percent)) \
.SetDisplay("Stop Loss", "Stop loss percent or value", "Risk Management")
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Candle type for strategy", "General")
self._cooldown = 0
@property
def CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
@property
def CciOversold(self):
return self._cci_oversold.Value
@CciOversold.setter
def CciOversold(self, value):
self._cci_oversold.Value = value
@property
def CciOverbought(self):
return self._cci_overbought.Value
@CciOverbought.setter
def CciOverbought(self, value):
self._cci_overbought.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(vwap_cci_strategy, self).OnReseted()
self._cooldown = 0
def OnStarted2(self, time):
super(vwap_cci_strategy, self).OnStarted2(time)
# Create indicators
vwap = VolumeWeightedMovingAverage()
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
# Setup candle subscription
subscription = self.SubscribeCandles(self.CandleType)
# Bind indicators to candles
subscription.Bind(vwap, cci, self.ProcessCandle).Start()
# Setup chart visualization if available
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, vwap)
cci_area = self.CreateChartArea()
if cci_area is not None:
self.DrawIndicator(cci_area, cci)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, vwap_value, cci_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
# Current price
price = float(candle.ClosePrice)
vwap_val = float(vwap_value)
# Determine if price is above or below VWAP
isPriceAboveVWAP = price > vwap_val
if self._cooldown > 0:
self._cooldown -= 1
return
# Trading rules - with tolerance bands like CS
belowVwap = price <= vwap_val * 1.001
aboveVwap = price >= vwap_val * 0.999
if belowVwap and cci_value <= self.CciOversold and self.Position == 0:
# Buy signal - price below VWAP and CCI oversold
self.BuyMarket()
self._cooldown = self.CooldownBars
elif aboveVwap and cci_value >= self.CciOverbought and self.Position == 0:
# Sell signal - price above VWAP and CCI overbought
self.SellMarket()
self._cooldown = self.CooldownBars
# Exit conditions
elif isPriceAboveVWAP and self.Position > 0:
# Exit long position when price crosses above VWAP
self.SellMarket()
self._cooldown = self.CooldownBars
elif not isPriceAboveVWAP and self.Position < 0:
# Exit short position when price crosses below VWAP
self.BuyMarket()
self._cooldown = self.CooldownBars
def CreateClone(self):
return vwap_cci_strategy()