Ver no GitHub

WE TRUST Channel Strategy

Overview

The WE TRUST Channel Strategy is a high-level StockSharp port of the MetaTrader 5 expert advisor "WE TRUST". The system trades pullbacks toward a linear weighted moving average that is surrounded by standard deviation bands. When price closes outside the bands the strategy anticipates mean reversion and opens a market position back toward the middle of the channel. Signal reversal, optional closing of opposite trades, and pip-based money management parameters mirror the original expert.

Trading Logic

  1. Subscribe to the configured candle type (hourly candles by default) and calculate two indicators on the selected price source:
    • A linear weighted moving average (LWMA) with configurable period and shift.
    • A standard deviation envelope with its own period and shift.
  2. Convert pip-based offsets into absolute price distances using the instrument PriceStep. Five-digit and three-digit quotes multiply the step by 10 to emulate the MetaTrader definition of a pip.
  3. Compute the upper and lower channel limits: LWMA ± StdDev ± ChannelIndentPips (converted into price units).
  4. Evaluate finished candles only. When the chosen candle price closes below the lower channel the strategy generates a buy signal. When it closes above the upper channel it generates a sell signal.
  5. Optionally invert the signals when ReverseSignals is enabled. Optionally flatten an opposite position before acting on a new signal when CloseOpposite is enabled.
  6. Submit market orders with the configured volume whenever the current position is flat or aligned with the signal direction.

Risk Management

  • StopLossPips and TakeProfitPips translate pip distances into absolute protective orders via StartProtection. Set them to 0 to disable the respective level.
  • TrailingStopPips and TrailingStepPips control a pip-based trailing stop that follows profitable trades. Both parameters convert into price distances using the same pip size logic.
  • All exits are performed with market orders to stay close to the MQL5 implementation.

Parameters

Parameter Description Default
OrderVolume Trade volume submitted with each market order. 0.1
StopLossPips Stop-loss distance expressed in pips (0 disables the stop). 40
TakeProfitPips Take-profit distance expressed in pips (0 disables the target). 60
TrailingStopPips Trailing stop distance in pips. 10
TrailingStepPips Trailing step in pips between stop adjustments. 10
MaPeriod Period of the linear weighted moving average. 60
MaShift Number of bars that the moving average is shifted forward. 0
StdDevPeriod Period of the standard deviation calculation. 50
StdDevShift Number of bars that the deviation value is shifted. 0
SignalBarOffset Number of completed bars to look back when evaluating signals. 1
ChannelIndentPips Additional buffer added outside the deviation bands. 1
ReverseSignals Invert the buy/sell logic of the channel breakout. false
CloseOpposite Close an opposite position before entering a new trade. false
AppliedPrice Candle price component fed into both indicators. Weighted
CandleType Candle data type requested from the connector. 1 hour time frame

Notes

  • The strategy relies on valid PriceStep metadata. If the exchange does not provide it the code falls back to Security.Step and finally to 1.
  • Only the C# implementation is included in this directory. The Python port is intentionally omitted per instructions.
  • The logic processes finished candles only and does not attempt to accumulate partial bar data.
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;
	}
}