Открыть на GitHub

Стратегия пробоя канала по одной скользящей средней

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

Стратегия One MA Channel Breakout переносит логику советника MetaTrader 5 One MA EA на высокоуровневый API StockSharp. На графике строится смещённая скользящая средняя, вокруг которой формируется канал с расстояниями, задаваемыми в пунктах. Если в пределах одной свечи цена протестировала канал и открылась за его пределами, стратегия открывает позицию в сторону пробоя. Риск контролируется автоматически за счёт настраиваемых стоп-лосса и тейк-профита.

Основные особенности:

  • Поддержка нескольких типов скользящих средних (SMA, EMA, SMMA, LWMA).
  • Гибкий выбор цены, подаваемой на вход индикатора (close, open, high, low, median, typical, weighted).
  • Раздельные смещения для значения скользящей и для свечи, используемой при анализе, полностью повторяющие параметры Current Bar в оригинальном советнике.
  • Преобразование расстояний в пунктах в абсолютные ценовые величины с учётом PriceStep и количества знаков инструмента (для инструментов с 3/5 знаками используется классическое определение pip).

Алгоритм работы

  1. Подготовка индикаторов

    • Рассчитывается скользящая средняя с периодом MaPeriod, методом MaMethodParam, смещением MaShift и выбранной ценой AppliedPriceType на свечах типа CandleType.
    • Верхняя и нижняя границы канала вычисляются как ChannelHighPips и ChannelLowPips пунктов от базовой линии.
    • Фиксированные буферы сохраняют историю значений, что позволяет обращаться к прошлым барам по индексам MaBarShift и PriceBarShift.
  2. Формирование сигналов

    • Пробой вверх: минимум анализируемой свечи находится между базовой линией и верхней границей канала, а цена открытия выше верхней границы. При отсутствии лонга (Position <= 0) выполняется покупка.
    • Пробой вниз: максимум свечи располагается между базовой линией и нижней границей, а открытие ниже нижней границы. При отсутствии шорта (Position >= 0) выполняется продажа.
    • Объём заявки равен параметру TradeVolume плюс объём, необходимый для закрытия встречной позиции, что имитирует поведение советника на хеджевом счёте.
  3. Управление риском

    • Значения StopLossPips и TakeProfitPips переводятся в абсолютные цены и передаются в StartProtection, которая выставляет защитные ордера.
    • Нулевые значения отключают соответствующую защиту.

Дополнительного выхода из позиции не предусмотрено: закрытие происходит защитными ордерами либо разворотом по встречному сигналу.

Параметры

Параметр Описание
MaPeriod Период скользящей средней (> 0).
MaShift Горизонтальное смещение скользящей в барах.
MaMethodParam Тип скользящей (Sma, Ema, Smma, Lwma).
AppliedPriceType Цена свечи, подаваемая в индикатор.
MaBarShift Индекс исторического значения скользящей (0 — текущий бар).
PriceBarShift Индекс свечи, по которой анализируется пробой.
ChannelHighPips Расстояние до верхней границы канала в пунктах.
ChannelLowPips Расстояние до нижней границы канала в пунктах.
StopLossPips Стоп-лосс в пунктах (0 — выключен).
TakeProfitPips Тейк-профит в пунктах (0 — выключен).
TradeVolume Торговый объём, синхронизированный с Strategy.Volume.
CandleType Тип/таймфрейм свечей, используемых в расчётах.

Особенности реализации

  • Пересчёт пунктов в цену: для инструментов с 3 или 5 знаками точка = PriceStep * 10, иначе используется PriceStep.
  • История хранится в фиксированных скользящих окнах, что избавляет от прямых вызовов GetValue и соответствует требованиям репозитория.
  • Обрабатываются только завершённые свечи, что устраняет ложные сигналы на незакрытых барах.
  • При наличии области на графике выводятся свечи и собственные сделки для наглядного анализа.

Рекомендации по применению

  • Убедитесь, что у инструмента корректно заданы PriceStep и Decimals; при отсутствии данных настройте пункты вручную.
  • Оптимизируйте MaPeriod, границы канала и смещения под конкретный рынок и таймфрейм.
  • В реальной торговле комбинируйте стратегию с портфельным управлением рисками, поскольку она удерживает по одному чистому направлению на инструмент.
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>
/// One MA channel breakout strategy.
/// Places an SMA channel around price with configurable offset, trades breakouts.
/// </summary>
public class OneMaChannelBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<decimal> _channelOffset;

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

	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	public decimal ChannelOffset
	{
		get => _channelOffset.Value;
		set => _channelOffset.Value = value;
	}

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

		_maPeriod = Param(nameof(MaPeriod), 44)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Moving average length", "Indicators");

		_channelOffset = Param(nameof(ChannelOffset), 0.005m)
			.SetGreaterThanZero()
			.SetDisplay("Channel Offset", "Percentage offset for channel", "Indicators");
	}

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

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

		var sma = new SimpleMovingAverage { Length = MaPeriod };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var upper = maValue * (1 + ChannelOffset);
		var lower = maValue * (1 - ChannelOffset);
		var close = candle.ClosePrice;

		// Bullish breakout above upper channel
		if (close > upper && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Bearish breakdown below lower channel
		else if (close < lower && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}