DoubleUp2 CCI MACD Strategy
DoubleUp2 is a martingale-style strategy combining the Commodity Channel Index (CCI) and MACD. It opens short positions when both indicators show extreme positive values and long positions when both are extremely negative. After a losing trade the position size doubles, seeking to recover previous losses. Profitable trades are closed once price advances by a fixed number of points.
Details
- Entry Criteria:
- Long:
CCI < -ThresholdandMACD < -Threshold. - Short:
CCI > ThresholdandMACD > Threshold.
- Long:
- Long/Short: Both.
- Exit Criteria:
- Opposite signal or price moves
ExitDistancepoints in profit.
- Opposite signal or price moves
- Stops: No explicit stop loss.
- Default Values:
CCI Period= 8MACD Fast= 13MACD Slow= 33MACD Signal= 2Threshold= 230Base Volume= 0.1ExitDistance=120 * price step
- Filters:
- Category: Mean reversion
- Direction: Both
- Indicators: CCI, MACD
- Stops: No
- Complexity: Medium
- Timeframe: Short-term
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: High
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>
/// DoubleUp2 strategy combining CCI and MACD with volume doubling (martingale).
/// </summary>
public class DoubleUp2Strategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<decimal> _threshold;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private int _martingaleStep;
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
/// <summary>
/// MACD fast EMA period.
/// </summary>
public int MacdFastPeriod { get => _macdFastPeriod.Value; set => _macdFastPeriod.Value = value; }
/// <summary>
/// MACD slow EMA period.
/// </summary>
public int MacdSlowPeriod { get => _macdSlowPeriod.Value; set => _macdSlowPeriod.Value = value; }
/// <summary>
/// Threshold for CCI and MACD signals.
/// </summary>
public decimal Threshold { get => _threshold.Value; set => _threshold.Value = value; }
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Constructor.
/// </summary>
public DoubleUp2Strategy()
{
_cciPeriod = Param(nameof(CciPeriod), 8)
.SetDisplay("CCI Period", "Averaging period for CCI", "Indicators")
.SetOptimize(4, 20, 1);
_macdFastPeriod = Param(nameof(MacdFastPeriod), 13)
.SetDisplay("MACD Fast", "Fast EMA period for MACD", "Indicators")
.SetOptimize(5, 20, 1);
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 33)
.SetDisplay("MACD Slow", "Slow EMA period for MACD", "Indicators")
.SetOptimize(20, 50, 1);
_threshold = Param(nameof(Threshold), 70m)
.SetDisplay("Threshold", "RSI extreme level", "Strategy")
.SetOptimize(55m, 85m, 5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_martingaleStep = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = CciPeriod };
var macd = new MovingAverageConvergenceDivergence(
new ExponentialMovingAverage { Length = MacdSlowPeriod },
new ExponentialMovingAverage { Length = MacdFastPeriod });
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, macd, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal macdValue)
{
if (candle.State != CandleStates.Finished)
return;
var step = Security?.PriceStep ?? 1m;
var lowThreshold = 100m - Threshold;
// Short entry condition
if (rsiValue > Threshold && macdValue > 0m)
{
if (Position > 0)
{
var profit = candle.ClosePrice - _entryPrice;
_martingaleStep = profit > 0m ? 0 : _martingaleStep + 1;
}
SellMarket();
_entryPrice = candle.ClosePrice;
return;
}
// Long entry condition
if (rsiValue < lowThreshold && macdValue < 0m)
{
if (Position < 0)
{
var profit = _entryPrice - candle.ClosePrice;
_martingaleStep = profit > 0m ? 0 : _martingaleStep + 1;
}
BuyMarket();
_entryPrice = candle.ClosePrice;
return;
}
// Exit profitable long position
if (Position > 0 && candle.ClosePrice - _entryPrice > 120m * step)
{
SellMarket();
_martingaleStep += 2;
return;
}
// Exit profitable short position
if (Position < 0 && _entryPrice - candle.ClosePrice > 120m * step)
{
BuyMarket();
_martingaleStep += 2;
}
}
}
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 (
RelativeStrengthIndex,
MovingAverageConvergenceDivergence,
ExponentialMovingAverage,
)
from StockSharp.Algo.Strategies import Strategy
class double_up2_strategy(Strategy):
def __init__(self):
super(double_up2_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 8)
self._macd_fast_period = self.Param("MacdFastPeriod", 13)
self._macd_slow_period = self.Param("MacdSlowPeriod", 33)
self._threshold = self.Param("Threshold", 70.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._entry_price = 0.0
self._martingale_step = 0
@property
def CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
@property
def MacdFastPeriod(self):
return self._macd_fast_period.Value
@MacdFastPeriod.setter
def MacdFastPeriod(self, value):
self._macd_fast_period.Value = value
@property
def MacdSlowPeriod(self):
return self._macd_slow_period.Value
@MacdSlowPeriod.setter
def MacdSlowPeriod(self, value):
self._macd_slow_period.Value = value
@property
def Threshold(self):
return self._threshold.Value
@Threshold.setter
def Threshold(self, value):
self._threshold.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(double_up2_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._martingale_step = 0
rsi = RelativeStrengthIndex()
rsi.Length = self.CciPeriod
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.MacdSlowPeriod
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.MacdFastPeriod
macd = MovingAverageConvergenceDivergence(slow_ema, fast_ema)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, macd, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_value, macd_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
macd_val = float(macd_value)
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
threshold = float(self.Threshold)
low_threshold = 100.0 - threshold
if rsi_val > threshold and macd_val > 0.0:
if self.Position > 0:
profit = close - self._entry_price
if profit > 0.0:
self._martingale_step = 0
else:
self._martingale_step += 1
self.SellMarket()
self._entry_price = close
return
if rsi_val < low_threshold and macd_val < 0.0:
if self.Position < 0:
profit = self._entry_price - close
if profit > 0.0:
self._martingale_step = 0
else:
self._martingale_step += 1
self.BuyMarket()
self._entry_price = close
return
if self.Position > 0 and close - self._entry_price > 120.0 * step:
self.SellMarket()
self._martingale_step += 2
return
if self.Position < 0 and self._entry_price - close > 120.0 * step:
self.BuyMarket()
self._martingale_step += 2
def OnReseted(self):
super(double_up2_strategy, self).OnReseted()
self._entry_price = 0.0
self._martingale_step = 0
def CreateClone(self):
return double_up2_strategy()