This strategy is a StockSharp port of the MetaTrader 4 expert advisor "Crypto Analysis". It hunts for breakouts that occur after price tags the outer Bollinger Band on the main trading timeframe while the market structure remains bearish (fast LWMA below the slow LWMA). The system only allows trades when a higher timeframe momentum burst and a monthly MACD filter both agree with the desired direction. Once in the market, the position is managed by a layered protection block that mirrors the original EA: pip-based stops, money-based trailing, break-even relocation, and portfolio drawdown controls.
Trading logic
Signal timeframe: configurable (default M15). All entry/exit rules are evaluated on these candles.
Volatility trigger: previous candle low must touch or pierce the lower Bollinger Band (20, 2) to prepare a long setup, while a touch of the upper band prepares a short setup.
Trend filter: both scenarios require the fast linear weighted moving average (LWMA, default 6) to stay below the slow LWMA (default 85), replicating the bearish bias check in the EA.
RSI confirmation: RSI(14) has to be above 50 for longs and below 50 for shorts.
Momentum burst: the maximum absolute deviation of the last three higher-timeframe Momentum(14) values from the 100 baseline must exceed the buy/sell thresholds. This captures the momentum spikes used by the MQL code.
Monthly MACD filter: a separate monthly (default 30-day candles) MACD (12, 26, 9) confirms direction; longs require MACD main above signal, shorts demand the opposite.
Entry execution: once all filters align, the strategy opens a market order. Opposite positions are flattened before reversing to keep a single net position, which mirrors the EA’s behaviour of closing opposing trades.
Position management
Initial stop and target: configurable distances in pips are converted from the instrument tick size using the same 5-digit/3-digit handling as the EA (0.00001 and 0.001 steps are multiplied by 10).
Trailing stop: after a new high (long) or low (short) forms, the stop is pulled behind price by TrailingStopPips, always respecting the best level achieved.
Break-even: when profit reaches BreakEvenTriggerPips, the stop is moved to the entry price plus BreakEvenOffsetPips (long) or minus the offset (short).
Money targets: optional absolute or percentage-based profit caps close the position as soon as the floating PnL hits the requested level.
Money trailing: once unrealized profit exceeds MoneyTrailTarget, the strategy tracks the peak and closes the position if the giveback equals or exceeds MoneyTrailStop.
Equity stop: the floating equity (current portfolio value plus unrealized PnL) is monitored; if the drawdown from the peak surpasses EquityRiskPercent, the position is flattened.
Multi-timeframe data
Three subscriptions are registered automatically:
Primary candle series for the Bollinger/LWMA/RSI rules.
Higher timeframe candles for the momentum filter (default H1).
Monthly candles for the MACD confirmation (default 30-day bars).
Parameters
Parameter
Description
OrderVolume
Base order size. Opposite positions are closed before opening a new one.
UseMoneyTakeProfit
Enable the absolute monetary take-profit target.
MoneyTakeProfit
Profit in portfolio currency that triggers an exit when UseMoneyTakeProfit is true.
UsePercentTakeProfit
Enable the percent-based take-profit target calculated from the initial equity.
PercentTakeProfit
Profit percentage required to close the position when the percent target is active.
EnableMoneyTrailing
Activates the money-based trailing block.
MoneyTrailTarget
Profit level that starts the money trail.
MoneyTrailStop
Maximum allowed profit giveback after MoneyTrailTarget has been reached.
StopLossPips
Initial stop-loss distance in pips.
TakeProfitPips
Initial take-profit distance in pips.
TrailingStopPips
Trailing stop distance in pips.
UseBreakEven
Enable the break-even stop relocation.
BreakEvenTriggerPips
Pip profit required before break-even protection activates.
BreakEvenOffsetPips
Additional pips added to the entry price when placing the break-even stop.
FastMaPeriod
Length of the fast LWMA calculated on typical price.
SlowMaPeriod
Length of the slow LWMA calculated on typical price.
MomentumPeriod
Period of the Momentum indicator on the higher timeframe.
MomentumBuyThreshold
Minimum momentum deviation for long entries.
MomentumSellThreshold
Minimum momentum deviation for short entries.
MacdFastLength
Fast EMA length for the higher timeframe MACD filter.
MacdSlowLength
Slow EMA length for the higher timeframe MACD filter.
MacdSignalLength
Signal length for the higher timeframe MACD filter.
UseEquityStop
Enable portfolio drawdown protection.
EquityRiskPercent
Allowed equity drawdown percentage before forcefully closing the position.
CandleType
Primary timeframe used for entries.
MomentumCandleType
Higher timeframe used for momentum confirmation.
MacdCandleType
Higher timeframe used for MACD confirmation.
Notes
The StockSharp port keeps a single net position, matching the EA which closes opposite orders before opening a new trade.
All protective rules operate on closed candles to replicate the "new bar" processing in the original script.
When using synthetic symbols or instruments without a standard pip size, adjust StopLossPips and related parameters to the exchange’s tick value.
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 CryptoAnalysisStrategy : 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 CryptoAnalysisStrategy()
{
_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_analysis_strategy(Strategy):
def __init__(self):
super(crypto_analysis_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_analysis_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_analysis_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_analysis_strategy()