The Crypto SR strategy ports the MetaTrader 4 expert advisor "Crypto S&R" to the StockSharp high-level API. The implementation keeps the layered confirmation logic of the original system: a trend filter based on linear weighted moving averages (LWMA), a higher timeframe momentum check, a long-term MACD trend filter and fractal-derived support/resistance levels. Orders are submitted with market execution and the position is managed via fixed stop-loss/take-profit levels, break-even adjustments and a trailing stop measured in pips.
Trading logic
Primary timeframe analysis – the strategy subscribes to the configured candle series and feeds two LWMAs with the typical candle price (high + low + close) / 3. The fast LWMA must stay above (below) the slow LWMA to enable longs (shorts).
Higher timeframe momentum – a Momentum indicator is evaluated on a second candle series. The absolute distance of the latest three momentum readings from the neutral value (100) must exceed the buy/sell thresholds.
Long-term MACD filter – the strategy listens to another candle stream where a MACD (12, 26, 9) is calculated. Long positions require the MACD line to remain above its signal, short positions need it below the signal. The default long-term timeframe is daily to approximate the monthly series used by the EA; it can be adjusted if real monthly candles are available.
Fractal support/resistance – finished candles are stored in a rolling buffer. When the classic Bill Williams fractal pattern (two neighbours on each side) appears, the corresponding high/low becomes the active resistance or support level. A configurable pip buffer is applied around the level to emulate the horizontal lines drawn by the original expert.
Entry rules:
Buy: no open long position, fast LWMA above slow LWMA, momentum deviation ≥ buy threshold, MACD bullish, the current candle tests the buffered support and closes above the previous close.
Sell: mirror conditions with the resistance level, momentum sell threshold and MACD bearish confirmation.
Risk management – every new position receives an initial stop-loss and take-profit in pips. Break-even logic can shift the stop once the move reaches the trigger distance, while an optional trailing stop follows price using the candle highs/lows. Long/short exposure is closed if the MACD filter flips against the trade.
Implementation notes
The monthly MACD filter from the MetaTrader version is approximated with a daily series by default because StockSharp does not provide calendar-month candles out of the box. Users can switch to a custom monthly aggregator if their data source supports it.
Orders are closed with market requests when protection levels are violated. This mirrors the OrderClose calls in MQL and avoids relying on exchange-side stop orders.
All indicator bindings are performed through the high-level subscription API, and no direct calls to GetValue are required.
Parameters
Name
Description
Default
FastMaPeriod
Length of the fast LWMA on the primary timeframe.
6
SlowMaPeriod
Length of the slow LWMA on the primary timeframe.
85
MomentumPeriod
Momentum period on the higher timeframe.
14
MomentumBuyThreshold
Minimum absolute deviation of momentum from 100 to enable long entries.
0.3
MomentumSellThreshold
Minimum absolute deviation of momentum from 100 to enable short entries.
0.3
MacdFastPeriod
Fast EMA length for the long-term MACD filter.
12
MacdSlowPeriod
Slow EMA length for the long-term MACD filter.
26
MacdSignalPeriod
Signal EMA length for the long-term MACD filter.
9
StopLossPips
Hard stop-loss distance expressed in pips.
20
TakeProfitPips
Fixed take-profit distance expressed in pips.
50
TrailingStopPips
Trailing stop distance in pips (0 disables the trail).
40
UseBreakEven
Whether to move the stop to break-even after a profit trigger.
true
BreakEvenTriggerPips
Profit in pips required before break-even adjustments are applied.
30
BreakEvenOffsetPips
Offset added when moving the stop to break-even.
30
FractalWindowLength
Number of finished candles retained to confirm fractal highs and lows.
7
FractalBufferPips
Additional buffer around fractal levels in pips.
10
TradeVolume
Volume submitted with each market order.
1
CandleType
Primary candle series for LWMA and fractal logic.
15m time frame
HigherCandleType
Higher timeframe for the momentum filter.
1h time frame
LongTermCandleType
Timeframe for the MACD trend filter.
1d time frame
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 CryptoSrStrategy : 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 CryptoSrStrategy()
{
_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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class crypto_sr_strategy(Strategy):
def __init__(self):
super(crypto_sr_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(crypto_sr_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(crypto_sr_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return crypto_sr_strategy()