Ver en GitHub

AOCCI Strategy

Overview

The AOCCI strategy is a direct conversion of the MetaTrader 4 "AOCCI" expert advisor. It combines momentum and mean reversion filters by using the Awesome Oscillator (AO) and the Commodity Channel Index (CCI) together with a daily floor pivot. The converted version works on StockSharp's high level API and keeps the same protective logic as the original script.

Logic

  1. Data preparation
    • Uses intraday candles (default 1 hour) for signal generation.
    • Uses daily candles to compute the pivot from the previous completed day (high + low + close divided by 3).
    • Tracks the last six intraday opening prices to detect large gaps.
  2. Gap filter
    • Any single step difference that exceeds the Big Jump Filter threshold cancels the current signal.
    • Any two-step combined difference that exceeds the Double Jump Filter threshold also cancels the signal.
  3. Indicator checks
    • AO must be greater than zero and CCI must be non-negative on the current bar.
    • At least one of the following must be true on the previous bar: AO below zero, CCI at or below zero, or price below the pivot.
  4. Directional filter
    • The close price must remain above the pivot level.
  5. Orders
    • The original expert advisor only opens long trades because the short condition duplicates the long logic. The conversion keeps this behaviour.
    • Market orders use the configured Order Volume.
  6. Protection
    • Initial stop-loss and take-profit are expressed in price steps.
    • Optional trailing stop tightens the stop once price moves in favour of the position by at least the trailing distance.

Parameters

Name Description
CciPeriod Period for the Commodity Channel Index (default 55).
SignalCandleOffset Additional offset applied when referencing historical daily candles (default 0).
StopLossPoints Stop-loss distance in price steps.
TakeProfitPoints Take-profit distance in price steps.
TrailingStopPoints Trailing stop distance in price steps (0 disables trailing).
BigJumpPoints Maximum allowed single-bar opening gap expressed in price steps.
DoubleJumpPoints Maximum allowed two-bar combined gap expressed in price steps.
OrderVolume Volume used when submitting market orders.
CandleType Intraday candle type (default one-hour bars).
DailyCandleType Daily candle type used for pivot calculation.

Usage Notes

  • The strategy requires both intraday and daily data subscriptions.
  • Price step (tick size) from the selected security is used to translate point-based risk parameters into actual prices.
  • Trailing stop management is applied on completed candles, mirroring the original EA's behaviour.
  • Because the original MQL4 version never triggers short trades, the conversion intentionally keeps the same rule set.
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>
/// AOCCI Pivot strategy - CCI crossover with momentum filter.
/// Buys when CCI crosses above 0 with positive momentum.
/// Sells when CCI crosses below 0 with negative momentum.
/// </summary>
public class AocciPivotFilterStrategy : Strategy
{
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevCci;
	private bool _hasPrev;

	public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AocciPivotFilterStrategy()
	{
		_cciPeriod = Param(nameof(CciPeriod), 14)
			.SetDisplay("CCI Period", "CCI period", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 10)
			.SetDisplay("Momentum Period", "Momentum 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 mom = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(cci, mom, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal cciValue, decimal momValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevCci = cciValue;
			_hasPrev = true;
			return;
		}

		// CCI crosses above 0 with positive momentum
		if (_prevCci <= 0 && cciValue > 0 && momValue > 0 && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// CCI crosses below 0 with negative momentum
		else if (_prevCci >= 0 && cciValue < 0 && momValue < 0 && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevCci = cciValue;
	}
}