Converts the MetaTrader 4 expert advisor "Anubis" to the StockSharp high level API.
Uses a 4-hour Commodity Channel Index (CCI) filter together with a 15-minute MACD crossover.
Applies adaptive position sizing, stop-loss, breakeven protection, ATR driven exits, and a standard deviation based take-profit.
Strategy logic
Data
Primary timeframe: 15-minute candles (SignalCandleType), used for MACD and ATR calculations.
Higher timeframe: 4-hour candles (TrendCandleType), used for CCI filtering and standard deviation measurement.
Indicators
CommodityChannelIndex with configurable period on the 4H series.
StandardDeviation (length 30) on 4H closes to estimate the take-profit distance.
MovingAverageConvergenceDivergenceSignal (fast/slow/signal configurable) on 15M candles.
AverageTrueRange (length 12) on 15M candles for volatility based exits.
Entries
Short: when 4H CCI is above CciThreshold, the previous two MACD values show a bearish crossover (MACD crossing below its signal), MACD was positive, there are no open longs, and the price has moved at least PriceFilterPoints since the last short entry.
Long: symmetric condition with CCI below -CciThreshold, MACD crossing upwards while negative, no open shorts, and the minimum distance filter satisfied.
Risk management
Base volume is defined by VolumeValue and is scaled by account equity (2× above 14k, 3.2× above 22k) and by LossFactor after a losing trade.
Maximum simultaneous trades per direction are limited by MaxLongTrades and MaxShortTrades.
Hard stop-loss placed virtually at StopLossPoints * PriceStep from the average entry price.
Breakeven activates once price advances by BreakevenPoints and immediately closes the position if price returns to the entry.
Exits
Standard deviation take-profit closes the position once price moves StdDevMultiplier * StdDev in favor.
Aggressive exits trigger when the prior candle range exceeds CloseAtrMultiplier * ATR.
MACD deceleration exits require both sufficient profit (ProfitThresholdPoints) and a reversal in MACD slope (previous MACD less than or greater than two bars ago, depending on direction).
Protective stop closes the trade if price pierces the stop-loss distance or falls back to entry after breakeven activation.
Parameters
Name
Description
VolumeValue
Base order volume.
CciThreshold
Absolute threshold for the 4H CCI filter.
CciPeriod
Period of the 4H CCI indicator.
StopLossPoints
Stop-loss distance in points.
BreakevenPoints
Profit in points required to arm breakeven.
MacdFastPeriod
Fast EMA period for MACD.
MacdSlowPeriod
Slow EMA period for MACD.
MacdSignalPeriod
Signal EMA period for MACD.
LossFactor
Volume multiplier applied after a losing trade.
MaxShortTrades
Maximum number of concurrent short entries.
MaxLongTrades
Maximum number of concurrent long entries.
CloseAtrMultiplier
ATR multiplier for early exits.
ProfitThresholdPoints
Additional profit buffer (points) before MACD exits.
StdDevMultiplier
Standard deviation multiplier for the take-profit.
PriceFilterPoints
Minimum price movement between consecutive entries.
SignalCandleType
Primary timeframe for MACD and ATR.
TrendCandleType
Higher timeframe for CCI and standard deviation.
Notes
The strategy relies on valid Security.PriceStep metadata to translate point-based parameters to price distances.
Protective logic is implemented via explicit checks instead of pending stop/limit orders, mirroring the original EA behavior with virtual stops.
Python version is intentionally omitted per task instructions.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Anubis CCI + MACD strategy.
/// Buys when CCI crosses above 0 and MACD histogram is positive.
/// Sells when CCI crosses below 0 and MACD histogram is negative.
/// </summary>
public class AnubisCciMacdStrategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevCci;
private bool _hasPrev;
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public AnubisCciMacdStrategy()
{
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetDisplay("CCI Period", "CCI lookback period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCci = 0m;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var cci = new CommodityChannelIndex { Length = CciPeriod };
var macd = new MovingAverageConvergenceDivergenceSignal();
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(cci, macd, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue cciValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!cciValue.IsFinal || !macdValue.IsFinal)
return;
var cci = cciValue.ToDecimal();
var macdVal = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdVal.Macd is not decimal macd || macdVal.Signal is not decimal signal)
return;
var histogram = macd - signal;
if (!_hasPrev)
{
_prevCci = cci;
_hasPrev = true;
return;
}
// CCI crosses above 0 with bullish MACD
if (_prevCci <= 0 && cci > 0 && histogram > 0 && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// CCI crosses below 0 with bearish MACD
else if (_prevCci >= 0 && cci < 0 && histogram < 0 && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevCci = cci;
}
}
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, CandleStates
from StockSharp.Algo.Indicators import CommodityChannelIndex, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class anubis_cci_macd_strategy(Strategy):
"""Anubis CCI + MACD strategy.
Buys when CCI crosses above 0 and MACD histogram is positive.
Sells when CCI crosses below 0 and MACD histogram is negative."""
def __init__(self):
super(anubis_cci_macd_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "CCI lookback period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_cci = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def CciPeriod(self):
return self._cci_period.Value
def OnReseted(self):
super(anubis_cci_macd_strategy, self).OnReseted()
self._prev_cci = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(anubis_cci_macd_strategy, self).OnStarted2(time)
self._has_prev = False
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
macd = MovingAverageConvergenceDivergenceSignal()
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(cci, macd, self._process_candle).Start()
def _process_candle(self, candle, cci_value, macd_value):
if candle.State != CandleStates.Finished:
return
if not cci_value.IsFinal or not macd_value.IsFinal:
return
cci = float(cci_value)
macd_raw = macd_value.Macd if hasattr(macd_value, 'Macd') else None
signal_raw = macd_value.Signal if hasattr(macd_value, 'Signal') else None
if macd_raw is None or signal_raw is None:
return
histogram = float(macd_raw) - float(signal_raw)
if not self._has_prev:
self._prev_cci = cci
self._has_prev = True
return
# CCI crosses above 0 with bullish MACD
if self._prev_cci <= 0 and cci > 0 and histogram > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# CCI crosses below 0 with bearish MACD
elif self._prev_cci >= 0 and cci < 0 and histogram < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_cci = cci
def CreateClone(self):
return anubis_cci_macd_strategy()