Открыть на GitHub

Стратегия WE TRUST Channel

Обзор

WE TRUST Channel — порт MetaTrader 5 советника «WE TRUST» на высокоуровневый API StockSharp. Стратегия торгует возврат цены к линейной взвешенной скользящей средней, вокруг которой построены полосы стандартного отклонения. Когда цена закрытия выходит за пределы канала, система ожидает возврат к среднему и открывает позицию в противоположную сторону. Поддерживаются инверсия сигналов, принудительное закрытие встречных позиций и управление рисками в пипсах — так же, как в оригинальном советнике.

Логика торговли

  1. Подписываемся на выбранный тип свечей (по умолчанию часовые) и рассчитываем два индикатора по заданной цене свечи:
    • Линейно-взвешенную скользящую среднюю (LWMA) с настраиваемым периодом и сдвигом.
    • Индикатор стандартного отклонения со своим периодом и сдвигом для построения канала.
  2. Переводим все значения в пипсах в абсолютные ценовые расстояния с помощью PriceStep инструмента. Для пяти- и трёхзнаковых котировок шаг цены умножается на 10, что повторяет определение пипса в MetaTrader.
  3. Формируем границы канала: LWMA ± StdDev ± ChannelIndentPips (в ценовых единицах).
  4. Обрабатываем только завершённые свечи. Если выбранная цена закрытия опускается ниже нижней границы, формируется покупка; если выше верхней — продажа.
  5. При включённом ReverseSignals направление сигналов меняется на противоположное. Параметр CloseOpposite позволяет закрывать встречную позицию перед открытием новой сделки.
  6. Рыночные заявки отправляются только тогда, когда текущая позиция отсутствует или совпадает по направлению с сигналом.

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

  • StopLossPips и TakeProfitPips конвертируются в абсолютные расстояния и передаются в StartProtection. Значение 0 отключает соответствующий уровень.
  • TrailingStopPips и TrailingStepPips задают пипсовый трейлинг-стоп, который сопровождает прибыльные позиции. Расчёт выполняется с использованием той же величины пипса.
  • Все выходы осуществляются рыночными заявками для максимального соответствия MQL5-реализации.

Параметры

Параметр Описание Значение по умолчанию
OrderVolume Объём каждой рыночной заявки. 0.1
StopLossPips Дистанция стоп-лосса в пипсах (0 отключает стоп). 40
TakeProfitPips Дистанция тейк-профита в пипсах (0 отключает цель). 60
TrailingStopPips Размер трейлинг-стопа в пипсах. 10
TrailingStepPips Шаг между перестановками трейлинг-стопа. 10
MaPeriod Период линейно-взвешенной скользящей средней. 60
MaShift Количество баров сдвига средней. 0
StdDevPeriod Период расчёта стандартного отклонения. 50
StdDevShift Сдвиг значения стандартного отклонения. 0
SignalBarOffset Сколько завершённых баров отступать назад при анализе. 1
ChannelIndentPips Дополнительный буфер за пределами полос. 1
ReverseSignals Инвертировать направления сигналов. false
CloseOpposite Закрывать встречную позицию перед входом. false
AppliedPrice Тип цены свечи, подаваемый в индикаторы. Weighted
CandleType Тип свечей, запрашиваемый у коннектора. Таймфрейм 1 час

Примечания

  • Стратегия использует PriceStep инструмента. При отсутствии значения выполняется поэтапный откат к Security.Step, затем к 1.
  • В каталоге присутствует только реализация на C#. Python-версия намеренно не создаётся согласно задаче.
  • Обработка ведётся исключительно по завершённым свечам; частичные бары не аккумулируются.
using System;
using System.Collections.Generic;

using Ecng.Common;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class WeTrustChannelStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public WeTrustChannelStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}