The Cryptos Strategy is a high-level StockSharp port of the original MetaTrader4 expert advisor cryptos.mq4. It focuses on the ETH/USD pair, combining Bollinger Bands with a linear weighted moving average (LWMA) to capture breakouts from volatility compression. The strategy tracks swing highs and lows across a configurable number of candles and dynamically calculates take-profit targets as a multiple of the detected range.
Trading Logic
Trend detection – when the close price touches the upper Bollinger band the strategy switches into a short bias, and when the lower band is touched it switches into a long bias. The band touch also freezes the current swing values by disabling automatic high/low updates.
Entry conditions –
Open a short position when the close price falls below the LWMA, the bias is short, and there is no active short position.
Open a long position when the close price rises above the LWMA, the bias is long, and there is no active long position.
Range projection – swing highs and lows (either automatic or manually frozen) define the distance from the LWMA. This distance, expressed in ticks, is multiplied by the take-profit ratio to compute profit targets and the risk-based position size.
Risk control – the strategy sets per-trade take-profit and stop-loss levels. For longs, the stop is placed below the swing low; for shorts, above the swing high. Stops and targets are recalculated for each entry and enforced inside the strategy loop.
Trailing exits – if a long position closes below the lower Bollinger band (or a short above the upper band), the open position is flattened immediately, mimicking the trailing behaviour of the original EA.
Parameters
Parameter
Description
CandleType
Data type of the candle series used for all indicator calculations.
BollingerPeriod, BollingerWidth
Length and standard deviation multiplier of the Bollinger Bands.
MaPeriod
Period of the linear weighted moving average based on median prices.
LookbackCandles
Number of candles examined to determine the automatic swing high/low.
TakeProfitRatio
Range multiplier used for profit targets when trading ETH/USD.
AlternativeTakeProfitRatio
Range multiplier applied to all other symbols.
RiskPerTrade
Amount of capital (in quote currency) that the volume calculator tries to risk on each trade.
ValueIndex, CryptoValueIndex
Multipliers converting risk into volume for non-crypto and crypto symbols respectively.
MinVolume, MaxVolume
Hard bounds for position size after alignment to exchange volume steps.
MinRangeTicks
Minimum allowed projected range in ticks to avoid zero-distance stops.
SpreadPoints
Manual override of the spread in ticks (auto-detected from best bid/ask if available).
GlobalTrend
Manual bias override: 1 forces a short setup, 2 forces a long setup, 0 lets the strategy decide.
AutoHighLow
When enabled, swing points are recalculated on every candle; when disabled they are frozen until the next band touch.
ManualBuyTrigger, ManualSellTrigger
Set to true to request an immediate long or short entry (reset after execution).
SkipBuys, SkipSells
Disable opening new long or short positions.
Position Sizing
The strategy replicates the MT4 logic: volume = RiskPerTrade / rangeTicks * valueIndex. The result is aligned to VolumeStep, then clipped between MinVolume/MaxVolume and the instrument’s exchange-imposed limits.
Usage Notes
The strategy checks portfolio value on start. If the balance is lower than RiskPerTrade * 3, trading is disabled and a warning is logged, matching the EA’s safety check.
Manual triggers and bias controls make it possible to synchronise with discretionary decisions during live trading.
ETH/USD automatically uses CryptoValueIndex and TakeProfitRatio; other instruments fall back to the alternative parameters.
Stops and targets are enforced within the strategy loop, so no additional protection module is required.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "Cryptos" MetaTrader expert.
/// Uses Bollinger Bands with a WMA trend filter. Buys when price is below WMA
/// and touches lower band; sells when price is above WMA and touches upper band.
/// </summary>
public class CryptosStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _wmaPeriod;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerWidth;
private ExponentialMovingAverage _bandEma;
private ExponentialMovingAverage _trendEma;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int WmaPeriod
{
get => _wmaPeriod.Value;
set => _wmaPeriod.Value = value;
}
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
public CryptosStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signals", "General");
_wmaPeriod = Param(nameof(WmaPeriod), 55)
.SetGreaterThanZero()
.SetDisplay("WMA Period", "Weighted moving average period", "Indicators");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bandEma = new ExponentialMovingAverage { Length = BollingerPeriod };
_trendEma = new ExponentialMovingAverage { Length = WmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_bandEma, _trendEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bandEma);
DrawIndicator(area, _trendEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal bandValue, decimal trendValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_bandEma.IsFormed || !_trendEma.IsFormed)
return;
var close = candle.ClosePrice;
var bandOffset = bandValue * (BollingerWidth / 100m);
var upperBand = bandValue + bandOffset;
var lowerBand = bandValue - bandOffset;
var volume = Volume;
if (volume <= 0)
volume = 1;
// Buy: price below WMA, touches lower band
if (close < trendValue && close <= lowerBand)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
// Sell: price above WMA, touches upper band
else if (close > trendValue && close >= upperBand)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
// Exit at WMA cross
if (Position > 0 && close >= upperBand)
SellMarket(Position);
else if (Position < 0 && close <= lowerBand)
BuyMarket(Math.Abs(Position));
}
/// <inheritdoc />
protected override void OnReseted()
{
_bandEma = null;
_trendEma = null;
base.OnReseted();
}
}
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 cryptos_strategy(Strategy):
def __init__(self):
super(cryptos_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._wma_period = self.Param("WmaPeriod", 55)
self._bollinger_period = self.Param("BollingerPeriod", 20)
self._bollinger_width = self.Param("BollingerWidth", 2.0)
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def WmaPeriod(self):
return self._wma_period.Value
@WmaPeriod.setter
def WmaPeriod(self, value):
self._wma_period.Value = value
@property
def BollingerPeriod(self):
return self._bollinger_period.Value
@BollingerPeriod.setter
def BollingerPeriod(self, value):
self._bollinger_period.Value = value
@property
def BollingerWidth(self):
return self._bollinger_width.Value
@BollingerWidth.setter
def BollingerWidth(self, value):
self._bollinger_width.Value = value
def OnReseted(self):
super(cryptos_strategy, self).OnReseted()
def OnStarted2(self, time):
super(cryptos_strategy, self).OnStarted2(time)
band_ema = ExponentialMovingAverage()
band_ema.Length = self.BollingerPeriod
trend_ema = ExponentialMovingAverage()
trend_ema.Length = self.WmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(band_ema, trend_ema, self._process_candle).Start()
def _process_candle(self, candle, band_value, trend_value):
if candle.State != CandleStates.Finished:
return
band_val = float(band_value)
trend_val = float(trend_value)
close = float(candle.ClosePrice)
band_offset = band_val * (float(self.BollingerWidth) / 100.0)
upper_band = band_val + band_offset
lower_band = band_val - band_offset
# Buy: price below WMA, touches lower band
if close < trend_val and close <= lower_band:
if self.Position <= 0:
self.BuyMarket()
# Sell: price above WMA, touches upper band
elif close > trend_val and close >= upper_band:
if self.Position >= 0:
self.SellMarket()
# Exit at opposite band
if self.Position > 0 and close >= upper_band:
self.SellMarket()
elif self.Position < 0 and close <= lower_band:
self.BuyMarket()
def CreateClone(self):
return cryptos_strategy()