Открыть на GitHub

Стратегия CCI and Martin

Общее описание

Стратегия CCI and Martin ищет резкий разворот после короткой серии однонаправленных свечей и подтверждает сигнал индикатором Commodity Channel Index. Логика повторяет оригинальный советник MetaTrader 5, но использует высокоуровневый API StockSharp. Алгоритм работает только с закрытыми свечами и может применяться к любым инструментам, для которых доступны CCI и шаг цены.

Правила входа

  • Покупка
    • Свечи -2 и -1 должны быть медвежьими (цена открытия выше цены закрытия).
    • Свеча 0 должна закрыться выше своей цены открытия и выше цены открытия свечи -1.
    • CCI на свече -1 должен быть ниже +5, ниже значения свечи -2, а CCI на свечах -2 и -3 должен последовательно снижаться. Текущее значение CCI (свеча 0) должно развернуться вверх и превысить предыдущий уровень.
    • Если все условия выполняются и позиция отсутствует, открывается длинная позиция.
  • Продажа
    • Свечи -2 и -1 должны быть бычьими (цена открытия ниже цены закрытия).
    • Свеча 0 должна закрыться ниже своей цены открытия и ниже цены открытия свечи -1.
    • CCI на свече -1 должен быть выше -5, выше значения свечи -2, а CCI на свечах -2 и -3 должен последовательно расти. Текущее значение CCI (свеча 0) должно развернуться вниз и опуститься ниже предыдущего уровня.
    • Если все условия выполняются и позиция отсутствует, открывается короткая позиция.

Изначально советник ждал 40 секунд после начала новой минуты, чтобы исключить «сырые» значения. В реализации на StockSharp анализируются только завершённые свечи, поэтому дополнительная задержка не требуется.

Управление рисками

  • Стоп-лосс и тейк-профит задаются в пунктах. При расчёте они переводятся в абсолютное смещение по цене: шаг цены умножается на десять для инструментов с трёх- и пятизначной котировкой, что соответствует оригинальной логике расчёта пункта.
  • Трейлинг-стоп активируется, когда цена проходит расстояние, равное сумме трейлинг-стопа и шага. После активации стоп переносится вслед за ценой на величину трейлинг-стопа и обновляется только при движении больше заданного шага.
  • Нулевые значения стоп-лосса или тейк-профита отключают соответствующие выходы. Для работы трейлинга необходимо указать положительные значения и дистанции, и шага.

Управление объёмом

Доступны два независимых механизма изменения объёма позиции.

  • Мартингейл умножает текущий объём на заданный коэффициент, когда количество последовательных убыточных сделок достигает порога. Увеличение объёма ограничено максимальным числом шагов. Любая прибыльная сделка возвращает объём к исходному значению.
  • Пошаговое увеличение прибавляет фиксированное значение к объёму либо после убыточной сделки, либо после прибыльной (режим задаётся параметром). Прирост нормализуется по шагу объёма инструмента и ограничивается максимальным объёмом. При превышении лимита или отсутствии сигнала объём возвращается к стартовому уровню.

Как и в оригинальном советнике, одновременное включение мартингейла и пошагового увеличения запрещено и контролируется кодом.

Параметры

  • CandleType – тип свечей для анализа.
  • CciPeriod – период сглаживания индикатора CCI.
  • InitialVolume – базовый объём сделки.
  • StopLossPips – стоп-лосс в пунктах.
  • TakeProfitPips – тейк-профит в пунктах.
  • TrailingStopPips – дистанция трейлинг-стопа в пунктах (0 отключает трейлинг).
  • TrailingStepPips – минимальное улучшение цены для переноса трейлинг-стопа.
  • EnableMartingale – включить мартингейл после убыточных сделок.
  • MartingaleCoefficient – коэффициент умножения объёма при мартингейле.
  • MartingaleTriggerLosses – сколько подряд убыточных сделок требуется для активации.
  • MartingaleMaxSteps – максимальное количество шагов мартингейла.
  • EnableStepAdjustments – включить пошаговое изменение объёма.
  • StepVolumeIncrement – величина прибавки к объёму при срабатывании правила.
  • StepVolumeMax – верхний предел объёма для пошагового режима.
  • StepAdjustmentMode – режим срабатывания (после убытка или после прибыли).

Примечания

  • Предполагается, что рыночные заявки исполняются близко к указанной цене. Для имитации тикового трейлинга из оригинального советника стопы пересчитываются на каждой завершённой свече.
  • Если шаг цены инструмента отличается от стандартных валютных котировок, пересчёт пунктов всё равно выполняется, но денежная оценка пунктов может отличаться.
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>
/// CCI based strategy with martingale-style entry.
/// Buys when CCI crosses above oversold level, sells when CCI crosses below overbought level.
/// </summary>
public class CCIAndMartinStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _cciPeriod;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<decimal> _oversold;

	private decimal? _prevCci;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int CciPeriod
	{
		get => _cciPeriod.Value;
		set => _cciPeriod.Value = value;
	}

	public decimal Overbought
	{
		get => _overbought.Value;
		set => _overbought.Value = value;
	}

	public decimal Oversold
	{
		get => _oversold.Value;
		set => _oversold.Value = value;
	}

	public CCIAndMartinStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_cciPeriod = Param(nameof(CciPeriod), 27)
			.SetGreaterThanZero()
			.SetDisplay("CCI Period", "CCI indicator length", "Indicators");

		_overbought = Param(nameof(Overbought), 100m)
			.SetDisplay("Overbought", "CCI overbought level", "Indicators");

		_oversold = Param(nameof(Oversold), -100m)
			.SetDisplay("Oversold", "CCI oversold level", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevCci = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevCci = null;

		var cci = new CommodityChannelIndex { Length = CciPeriod };

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

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, cci);
			DrawOwnTrades(area);
		}
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevCci = cciValue;
			return;
		}

		if (_prevCci == null)
		{
			_prevCci = cciValue;
			return;
		}

		var prev = _prevCci.Value;
		_prevCci = cciValue;

		// Buy signal: CCI crosses above oversold level from below
		if (prev < Oversold && cciValue >= Oversold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Sell signal: CCI crosses below overbought level from above
		else if (prev > Overbought && cciValue <= Overbought && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}