The Para Retrace Strategy is a C# conversion of the original MetaTrader 4 expert advisor Para_Retrace.mq4. It reproduces the idea of using the Parabolic SAR indicator as a dynamic anchor and waiting for price retracements back toward that level before entering the market. The conversion leverages the high-level StockSharp API to manage market data subscriptions, indicator updates, and order execution.
Trading Logic
Calculate the Parabolic SAR value on every finished candle using the configured acceleration step and maximum acceleration.
Determine the prevailing trend by checking whether the whole candle is below or above the SAR value:
Bearish context: if both the candle high and low are below the SAR value.
Bullish context: otherwise (price is touching or above the SAR level).
Derive a trigger price by offsetting the SAR value by a user-defined number of pips:
In a bearish context the strategy subtracts the offset, waiting for a retracement upward.
In a bullish context the strategy adds the offset, waiting for a pullback downward.
Once price touches the trigger (high crosses above for shorts, low crosses below for longs) the strategy opens a market order in the trend direction.
Protective stop-loss and take-profit orders are attached automatically using StockSharp's StartProtection facility, matching the distances from the original script.
Unlike the original expert advisor, the StockSharp version keeps trading after a position is opened; there is no need to manually reset the offset value. All actions are taken only on completed candles to avoid intrabar repainting issues.
Indicators
Parabolic SAR – drives both the trend detection and the entry levels.
Parameters
Parameter
Description
Default
SarStep
Initial acceleration factor for Parabolic SAR.
0.01
SarMax
Maximum acceleration factor for Parabolic SAR.
0.2
RetraceOffsetPips
Distance (in pips) between the SAR value and the entry trigger.
30
StopLossPips
Stop-loss distance in pips (converted to absolute price). Set to 0 to disable.
30
TakeProfitPips
Take-profit distance in pips (converted to absolute price). Set to 0 to disable.
30
CandleType
Timeframe used for candles and indicator calculations.
5 Minute
Note: The strategy estimates the pip size from the security metadata. If the instrument uses five decimal places (typical for Forex), one pip equals ten minimum price steps.
Order Management
Orders are placed at market once the retrace condition is satisfied.
The default trade size is one lot (Volume = 1), but this can be adjusted via the base Strategy.Volume property before starting the strategy.
StartProtection automatically manages stop-loss and take-profit placements using absolute price offsets derived from the pip settings.
Usage Tips
Tune the pip offset, stop, and target to match the volatility of the instrument being traded.
Consider pairing the strategy with additional filters (time-of-day, volatility, etc.) when integrating into a broader trading framework.
Always backtest before deploying live, as the profitability strongly depends on market conditions and broker execution.
Differences vs. Original Script
Continuous trading without manual global variables.
Uses completed candles instead of tick-by-tick checks, which provides deterministic behaviour for backtests.
Integrated risk management through StockSharp's protective order module.
Disclaimer
This strategy is provided for educational purposes. Test thoroughly on historical and demo data before committing real capital.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Para Retrace strategy. Uses WMA crossover (7/21).
/// </summary>
public class ParaRetraceStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast;
private decimal? _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public ParaRetraceStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 7).SetGreaterThanZero().SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 21).SetGreaterThanZero().SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new WeightedMovingAverage { Length = FastPeriod };
var slow = new WeightedMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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
class para_retrace_strategy(Strategy):
def __init__(self):
super(para_retrace_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast WMA", "Fast WMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow WMA", "Slow WMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(para_retrace_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(para_retrace_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = WeightedMovingAverage()
fast.Length = self.FastPeriod
slow = WeightedMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return para_retrace_strategy()