This strategy ports the 1 MINUTE SCALPER MetaTrader 4 expert advisor into the StockSharp high-level API. It keeps the multi-layer trend confirmation, multi-timeframe momentum, and long-term MACD filter of the original robot while adapting risk controls to StockSharp's position-centric model.
Core Logic
Trend Stack – thirteen linear weighted moving averages (LWMA 3/5/8/10/12/15/30/35/40/45/50/55/200) must align in strict order. Long trades require each shorter average to print above the next, while shorts invert the condition.
Primary Trend Gate – an additional fast LWMA (default 6) must stay above the slow LWMA (default 85) for longs and below for shorts, mirroring the EA's fast-vs-slow check.
Candle Structure – entries only trigger when the overlap patterns from the script are present: for longs the low two bars ago must be below the previous high, for shorts the previous low must dip under the high two bars back.
Momentum Filter – a 14-period momentum indicator calculated on a higher timeframe (default 15-minute candles) must deviate from 100 by at least the configured thresholds on any of the last three values. This reproduces the MomLevelB/MomLevelS comparisons.
Monthly MACD Bias – a MACD built on the selected MACD timeframe (defaults to 30-day candles as a proxy for monthly data) must show the main line above the signal line for longs or below for shorts.
Trade Management
Initial Protection – stop-loss and take-profit distances are expressed in instrument steps (points). When a position opens, the strategy converts these step counts to absolute prices using Security.PriceStep.
Break-Even Move – after price moves by BreakEvenTriggerSteps in favor, the stop is moved to the entry plus BreakEvenOffsetSteps (for shorts the mirrored logic applies). The flag is triggered once per position.
Step Trailing – when TrailingStopSteps is positive the stop follows the highest (or lowest) price since entry by the specified number of steps.
Money Trailing – once floating profit exceeds MoneyTrailTarget (currency), the strategy tracks the peak floating PnL and closes the position if the pullback equals MoneyTrailStop.
Money/Percent Targets – optional absolute or percentage take profits close all exposure when the floating PnL crosses the configured thresholds. The percentage target uses the initial portfolio value captured when the strategy starts.
Equity Stop – the strategy monitors the maximum equity (portfolio value plus open PnL). If the drawdown from that peak exceeds EquityRiskPercent, all positions are flattened, replicating the EA's AccountEquityHigh() safeguard.
Parameters
Parameter
Description
Volume
Order volume for new entries. Added to the absolute current position so reversals flip exposure immediately.
FastMaPeriod / SlowMaPeriod
LWMA lengths for the primary trend filter.
MomentumPeriod
Length of the higher timeframe momentum indicator.
MomentumBuyThreshold / MomentumSellThreshold
Minimum absolute deviation from 100 required for long/short momentum confirmation.
Enable the drawdown stop and define the maximum drawdown percentage.
CandleType
Primary working candles (defaults to 1 minute).
MomentumCandleType
Higher timeframe candles for the momentum indicator (defaults to 15 minutes).
MacdCandleType
Candles used for the MACD trend filter (defaults to 30 days ≈ monthly).
Differences vs. the MT4 Expert
StockSharp uses net positions, so the strategy always maintains a single aggregated position instead of multiple tickets up to Max_Trades. Reversals close the existing exposure before opening in the opposite direction.
PercentTakeProfit references the portfolio value captured at start instead of the constantly changing AccountBalance() used by MetaTrader, which avoids noisy targets when external trades modify the balance.
Money-based exit logic (Take_Profit_In_Money and TRAIL_PROFIT_IN_MONEY2) operates on the live floating PnL calculated from the strategy's average entry price. This matches the EA's behaviour but inside StockSharp's protection framework.
The platform must supply candle feeds for the selected timeframes (CandleType, MomentumCandleType, MacdCandleType). Ensure the adapters you use support the requested resolutions.
Tune the thresholds to fit your instrument and session. Narrow spreads or highly volatile pairs may require wider step distances or larger momentum thresholds to reduce noise.
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;
/// <summary>
/// One-minute scalper strategy using fast/slow WMA crossover.
/// Buys on bullish crossover, sells on bearish crossover.
/// </summary>
public class OneMinuteScalperStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private WeightedMovingAverage _fast;
private WeightedMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast WMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow WMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="OneMinuteScalperStrategy"/> class.
/// </summary>
public OneMinuteScalperStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast WMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 85)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow WMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 500)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new WeightedMovingAverage { Length = FastPeriod };
_slow = new WeightedMovingAverage { 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;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// WMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class one_minute_scalper_strategy(Strategy):
def __init__(self):
super(one_minute_scalper_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20).SetGreaterThanZero().SetDisplay("Fast Period", "Fast WMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 85).SetGreaterThanZero().SetDisplay("Slow Period", "Slow WMA period", "Indicator")
self._sl_points = self.Param("StopLossPoints", 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 500).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
def OnReseted(self):
super(one_minute_scalper_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
def OnStarted2(self, time):
super(one_minute_scalper_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
self._cooldown = 0
self._fast = WeightedMovingAverage()
self._fast.Length = self._fast_period.Value
self._slow = WeightedMovingAverage()
self._slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(tf(5))
sub.Bind(self._fast, self._slow, self.OnProcess).Start()
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
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 = candle.ClosePrice
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
step = float(self.Security.PriceStep)
# Check SL/TP
if self.Position > 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close <= self._entry_price - self._sl_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close >= self._entry_price + self._tp_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close >= self._entry_price + self._sl_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close <= self._entry_price - self._tp_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
# WMA crossover
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 = 60
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 = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return one_minute_scalper_strategy()