Открыть на GitHub

Стратегия Pipsover 8167

Обзор

Pipsover 8167 — это перенос на StockSharp советника MetaTrader 4 Pipsover.mq4 (сборка 8167). Советник ищет мощные всплески осциллятора Chaikin, возникающие сразу после отката предыдущей свечи к простой скользящей средней с периодом 20. При выполнении этих условий открывается позиция по направлению импульса, а защита осуществляется фиксированным стоп-лоссом и тейк-профитом (70 и 140 пунктов в оригинальном MQL-коде). Версия на C# воспроизводит ту же логику, используя высокоуровневые компоненты StockSharp без обращения к низкоуровневым буферам индикаторов.

Для восстановления значений Chaikin применяется линия накопления/распределения (ADL) и две экспоненциальные скользящие средние. Все торговые решения принимаются только после закрытия свечи, что повторяет проверки OrdersTotal() и обращения Close[1] / Open[1] из исходного скрипта.

Индикаторы и сигналы

  • Простая скользящая средняя (SMA 20) — рассчитывается по закрытиям свечей. Предыдущая свеча должна проколоть SMA (минимум ниже для покупок, максимум выше для продаж) при сохранении тела в сторону сигнала.
  • Осциллятор Chaikin (EMA 3 – EMA 10 от ADL) — пересчитывается из потока ADL и полностью соответствует значениям iCustom("Chaikin", 0, 0, 1) в MetaTrader. Пороговые уровни задаются в абсолютных единицах осциллятора.
  • Фильтр по свечному анализу — направление тела предыдущей свечи определяет допустимое направление сделки: бычья свеча разрешает покупки, медвежья — продажи.

Правила торговли

Вход в длинную позицию

  1. Предыдущая свеча закрылась выше открытия (Close[1] > Open[1]).
  2. Минимум предыдущей свечи ушёл ниже значения SMA20 на той же свече.
  3. Значение Chaikin на предыдущей свече ниже -OpenLevel (по умолчанию 55).
  4. Текущая позиция равна нулю.

Вход в короткую позицию

  1. Предыдущая свеча закрылась ниже открытия (Close[1] < Open[1]).
  2. Максимум предыдущей свечи находится выше значения SMA20.
  3. Chaikin на предыдущей свече превышает OpenLevel.
  4. Позиция отсутствует.

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

  • Длинные позиции закрываются, когда следующая свеча имеет медвежье тело, её максимум выше SMA20, а Chaikin превышает CloseLevel (по умолчанию 90).
  • Короткие позиции закрываются, если следующая свеча бычья, её минимум ниже SMA20, а Chaikin опускается ниже -CloseLevel.
  • Дополнительно каждая сделка сопровождается стоп-лоссом StopLossPoints и тейк-профитом TakeProfitPoints, рассчитанными в шагах цены выбранного инструмента.

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

  • Дистанция стоп-лосса: StopLossPoints × PriceStep (по умолчанию 70 пунктов).
  • Дистанция тейк-профита: TakeProfitPoints × PriceStep (по умолчанию 140 пунктов).
  • Объём позиции задаётся параметром TradeVolume, который напрямую назначается свойству Volume стратегии.

Параметры

Параметр Значение по умолчанию Описание
TradeVolume 0.1 Объём рыночных заявок (в лотах или контрактах).
MaLength 20 Период SMA, применяемой для фильтра откатов.
StopLossPoints 70 Дистанция стоп-лосса в шагах цены.
TakeProfitPoints 140 Дистанция тейк-профита в шагах цены.
OpenLevel 55 Абсолютный порог Chaikin для открытия позиций.
CloseLevel 90 Абсолютный порог Chaikin для принудительного выхода.
ChaikinFastLength 3 Период быстрой EMA при расчёте Chaikin.
ChaikinSlowLength 10 Период медленной EMA при расчёте Chaikin.
CandleType H1 Таймфрейм свечей, используемый в расчётах.

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

  • Связка свечей и индикаторов выполнена через SubscribeCandles().Bind(...), что полностью соответствует рекомендациям по использованию высокоуровневого API.
  • Значения Chaikin вычисляются в памяти путём подачи данных ADL на две EMA, поэтому не используются запрещённые операции GetValue().
  • Данные последней закрытой свечи кэшируются в полях стратегии, чтобы воспроизвести обращения Close[1], Low[1], High[1] и iCustom(...,1) из MQL.
  • Стоп-лосс и тейк-профит контролируются внутри стратегии, так как оригинальный советник отправлял только рыночные ордера со статическими отступами.
using System;







using StockSharp.Algo.Indicators;



using StockSharp.Algo.Strategies;



using StockSharp.BusinessEntities;



using StockSharp.Messages;







namespace StockSharp.Samples.Strategies;







public class Pipsover8167Strategy : Strategy



{



	private readonly StrategyParam<int> _emaPeriod;



	private readonly StrategyParam<DataType> _candleType;







	private decimal _prevClose; private decimal _prevEma; private bool _hasPrev;



	private int _cooldown;







	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }



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







	public Pipsover8167Strategy()



	{



		_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA lookback", "Indicators");



		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");



	}







	/// <inheritdoc />



	protected override void OnReseted()



	{



		base.OnReseted();



		_prevClose = default;



		_prevEma = default;



		_hasPrev = default;



		_cooldown = default;



	}







	/// <inheritdoc />



	/// <inheritdoc />

	protected override void OnStarted2(DateTime time)



	{



		base.OnStarted2(time);



		_hasPrev = false;



		var ema = new ExponentialMovingAverage { Length = EmaPeriod };



		var subscription = SubscribeCandles(CandleType);



		subscription.Bind(ema, ProcessCandle).Start();



	}







	private void ProcessCandle(ICandleMessage candle, decimal ema)



	{



		if (candle.State != CandleStates.Finished) return;



		if (!IsFormedAndOnlineAndAllowTrading()) return;



		var close = candle.ClosePrice;



		if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }



		if (_cooldown > 0)



		{



			_cooldown--;



			_prevClose = close; _prevEma = ema;



			return;



		}







		if (_prevClose <= _prevEma && close > ema && Position <= 0)



		{



			var volume = Volume + Math.Abs(Position);



			BuyMarket(volume);



			_cooldown = 6;



		}



		else if (_prevClose >= _prevEma && close < ema && Position >= 0)



		{



			var volume = Volume + Math.Abs(Position);



			SellMarket(volume);



			_cooldown = 6;



		}



		_prevClose = close; _prevEma = ema;



	}



}