Ver no GitHub

Crypto Scalper Strategy

The Crypto Scalper strategy reproduces the original MetaTrader expert logic with StockSharp high-level components. It watches for a bullish or bearish crossover of a fast linear weighted moving average on the primary timeframe and confirms the setup with trend filters calculated on a higher timeframe. Once the conditions align, the strategy enters using market orders and manages exits through stop-loss and take-profit distances measured in MetaTrader pips.

Parameters

Name Description Default
Primary Candle Candle type processed on the main timeframe. 1-minute time frame
Higher Candle Higher timeframe candle type used for confirmation. 15-minute time frame
Fast LWMA Length of the primary linear weighted moving average. 8
Higher Fast MA Fast LWMA length on the confirmation timeframe. 6
Higher Slow MA Slow LWMA length on the confirmation timeframe. 85
Momentum Period Momentum indicator length applied to higher timeframe candles. 14
Momentum Threshold Minimal deviation from the reference momentum (MetaTrader baseline 100) required for trading. 0.3
Momentum Reference Reference level used to emulate MetaTrader momentum scaling. 100
Stop Loss (pips) Protective stop distance in MetaTrader pips. 20
Take Profit (pips) Protective profit distance in MetaTrader pips. 50
Volume Order volume expressed in lots. 0.01
MACD Fast Fast EMA period for the MACD confirmation. 12
MACD Slow Slow EMA period for the MACD confirmation. 26
MACD Signal Signal EMA period for the MACD confirmation. 9

Trading Logic

  1. Subscribe to the primary timeframe and compute an LWMA that reacts to price quickly.
  2. Detect an entry when the previous candle crosses the LWMA up (long) or down (short).
  3. Confirm the crossover using the higher timeframe filters:
    • Higher fast LWMA must stay above the higher slow LWMA for long entries and below for short entries.
    • MACD histogram (main minus signal) needs to be positive for longs and negative for shorts.
    • Momentum must deviate from the reference level by at least Momentum Threshold.
  4. Send a market order in the detected direction when no other orders are active and the current position allows it.
  5. Monitor subsequent candles and close the position when either the stop-loss or the take-profit price is touched.

Notes

  • The strategy uses StockSharp high-level subscriptions with Bind, avoiding manual indicator buffers.
  • Protective levels are recalculated on every candle using the security price step. A fallback step of 0.0001 is applied if the instrument does not expose a configured price step.
  • Only one position is allowed at a time. Subsequent signals are ignored until the existing trade finishes.
  • All inline comments inside the C# implementation are written in English as required by the repository guidelines.
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 CryptoScalperStrategy : 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 CryptoScalperStrategy()
	{
		_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;
	}
}