Start
/
Strategie-Beispiele
Auf GitHub ansehen
CCI VWAP Strategy
The CCI VWAP approach attempts to capture intraday reversals when momentum and price align away from the volume weighted average price. By observing the Commodity Channel Index alongside the VWAP level, the system measures the strength of recent swings relative to a fair value benchmark.
Testing indicates an average annual return of about 70%. It performs best in the stocks market.
A buy setup emerges when the CCI falls below -100 and the market trades beneath VWAP, signalling that selling pressure may be exhausted. A short occurs when the CCI rises above +100 with price above VWAP, highlighting a stretched rally vulnerable to a setback. Positions are closed once price reclaims the VWAP in the opposite direction.
This strategy is designed for day traders who like to fade extremes yet still rely on objective levels for exits. The defined stop-loss helps manage risk if momentum does not quickly mean revert.
Details
Entry Criteria :
Long : CCI < -100 && Price < VWAP (oversold below VWAP)
Short : CCI > 100 && Price > VWAP (overbought above VWAP)
Long/Short : Both sides.
Exit Criteria :
Long : Exit long when price moves above VWAP
Short : Exit short when price falls below VWAP
Stops : Yes.
Default Values :
CciPeriod = 20
StopLossPercent = 2m
CandleType = TimeSpan.FromMinutes(5)
Filters :
Category: Mixed
Direction: Both
Indicators: CCI VWAP
Stops: Yes
Complexity: Intermediate
Timeframe: Intraday
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that uses CCI and VWAP indicators to identify oversold and overbought conditions.
/// Enters long when CCI is below -100 and price is below VWAP.
/// Enters short when CCI is above 100 and price is above VWAP.
/// </summary>
public class CciVwapStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private CommodityChannelIndex _cci;
private int _cooldown;
private DateTime _vwapDate;
private decimal _vwapCumPv;
private decimal _vwapCumVol;
/// <summary>
/// CCI period parameter.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop-loss percentage parameter.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public CciVwapStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CCI period", "CCI indicator period", "Indicators")
.SetOptimize(10, 30, 5);
_cooldownBars = Param(nameof(CooldownBars), 60)
.SetRange(1, 200)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop-loss %", "Stop-loss as percentage of entry price", "Risk Management")
.SetOptimize(1m, 3m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cci = null;
_cooldown = 0;
_vwapDate = default;
_vwapCumPv = 0m;
_vwapCumVol = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Initialize CCI indicator
_cci = new CommodityChannelIndex
{
Length = CciPeriod
};
var dummyEma = new ExponentialMovingAverage { Length = 10 };
// Bind CCI to candle subscription
var candlesSubscription = SubscribeCandles(CandleType)
.Bind(_cci, dummyEma, ProcessCandle)
.Start();
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, candlesSubscription);
DrawIndicator(area, _cci);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cci, decimal dummyValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
var date = candle.ServerTime.Date;
if (_vwapDate != date)
{
_vwapDate = date;
_vwapCumPv = 0m;
_vwapCumVol = 0m;
}
_vwapCumPv += candle.ClosePrice * candle.TotalVolume;
_vwapCumVol += candle.TotalVolume;
if (_vwapCumVol <= 0m)
return;
var currentVwap = _vwapCumPv / _vwapCumVol;
if (currentVwap == 0)
return;
if (_cooldown > 0)
_cooldown--;
// Long signal: CCI below -100 and price below VWAP
if (_cooldown == 0 && cci < -100 && candle.ClosePrice < currentVwap && Position <= 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (_cooldown == 0 && cci > 100 && candle.ClosePrice > currentVwap && Position >= 0)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice > currentVwap)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice < currentVwap)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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 CommodityChannelIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class cci_vwap_strategy(Strategy):
"""
CCI + VWAP strategy.
Enters long when CCI < -100 and price < VWAP.
Enters short when CCI > 100 and price > VWAP.
"""
def __init__(self):
super(cci_vwap_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 20) \
.SetDisplay("CCI period", "CCI indicator period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 60) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle type", "Type of candles to use", "General")
self._cooldown = 0
self._vwap_date = None
self._vwap_cum_pv = 0.0
self._vwap_cum_vol = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cci_vwap_strategy, self).OnReseted()
self._cooldown = 0
self._vwap_date = None
self._vwap_cum_pv = 0.0
self._vwap_cum_vol = 0.0
def OnStarted2(self, time):
super(cci_vwap_strategy, self).OnStarted2(time)
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
dummy_ema = ExponentialMovingAverage()
dummy_ema.Length = 10
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(cci, dummy_ema, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, cci)
self.DrawOwnTrades(area)
def on_process(self, candle, cci_val, dummy_val):
if candle.State != CandleStates.Finished:
return
date = candle.ServerTime.Date
if self._vwap_date != date:
self._vwap_date = date
self._vwap_cum_pv = 0.0
self._vwap_cum_vol = 0.0
self._vwap_cum_pv += float(candle.ClosePrice) * float(candle.TotalVolume)
self._vwap_cum_vol += float(candle.TotalVolume)
if self._vwap_cum_vol <= 0:
return
current_vwap = self._vwap_cum_pv / self._vwap_cum_vol
if current_vwap == 0:
return
if self._cooldown > 0:
self._cooldown -= 1
close = float(candle.ClosePrice)
if self._cooldown == 0 and cci_val < -100 and close < current_vwap and self.Position <= 0:
self.BuyMarket()
self._cooldown = self._cooldown_bars.Value
elif self._cooldown == 0 and cci_val > 100 and close > current_vwap and self.Position >= 0:
self.SellMarket()
self._cooldown = self._cooldown_bars.Value
elif self.Position > 0 and close > current_vwap:
self.SellMarket()
self._cooldown = self._cooldown_bars.Value
elif self.Position < 0 and close < current_vwap:
self.BuyMarket()
self._cooldown = self._cooldown_bars.Value
def CreateClone(self):
return cci_vwap_strategy()