RoNz Rapid-Fire Strategy
This strategy combines a moving average with the Parabolic SAR indicator to detect rapid trend changes. A long position is opened when the close price rises above the moving average while the Parabolic SAR flips below the price. A short position is opened on the opposite conditions. Positions can optionally be averaged when the trend continues.
How It Works
- Entry Long: Close price > SMA and Parabolic SAR switches below price.
- Entry Short: Close price < SMA and Parabolic SAR switches above price.
- Close: Either by stop loss/take profit or by opposite signal depending on the selected mode.
- Averaging: Adds new positions when the trend persists.
- Trailing Stop: Adjusts the stop price as the trade moves in profit.
Parameters
Volume– trade volume.StopLoss– stop loss in ticks.TakeProfit– take profit in ticks.TrailingStop– trailing stop in ticks.Averaging– enable averaging positions.MaPeriod– moving average period.PsarStep– Parabolic SAR step.PsarMax– Parabolic SAR maximum value.CloseType–SlCloseuses stops only,TrendClosecloses on opposite trend.CandleType– candle series for calculations.
Notes
- Works with any instrument supported by StockSharp.
- Requires historical candles for the selected
CandleType.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// RoNz Rapid-Fire strategy based on MA and Parabolic SAR trend signals.
/// </summary>
public class RoNzRapidFireStrategy : Strategy
{
public enum CloseTypes
{
SlClose,
TrendClose
}
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _trailingStop;
private readonly StrategyParam<bool> _averaging;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _psarStep;
private readonly StrategyParam<decimal> _psarMax;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<CloseTypes> _closeType;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
private decimal _prevClose;
private decimal _prevSma;
private decimal _tick;
private int _barsSinceTrade;
/// <summary>
/// Stop loss in ticks.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in ticks.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Trailing stop in ticks.
/// </summary>
public int TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Enable averaging.
/// </summary>
public bool Averaging
{
get => _averaging.Value;
set => _averaging.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Parabolic SAR step.
/// </summary>
public decimal PsarStep
{
get => _psarStep.Value;
set => _psarStep.Value = value;
}
/// <summary>
/// Parabolic SAR maximum.
/// </summary>
public decimal PsarMax
{
get => _psarMax.Value;
set => _psarMax.Value = value;
}
/// <summary>
/// Minimum number of bars between entries.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Close mode.
/// </summary>
public CloseTypes CloseType
{
get => _closeType.Value;
set => _closeType.Value = value;
}
/// <summary>
/// Candle series type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public RoNzRapidFireStrategy()
{
_stopLoss = Param(nameof(StopLoss), 150)
.SetDisplay("Stop Loss", "Stop loss in ticks", "Risk");
_takeProfit = Param(nameof(TakeProfit), 100)
.SetDisplay("Take Profit", "Take profit in ticks", "Risk");
_trailingStop = Param(nameof(TrailingStop), 0)
.SetDisplay("Trailing Stop", "Trailing stop in ticks", "Risk");
_averaging = Param(nameof(Averaging), false)
.SetDisplay("Averaging", "Add to position on continuing trend", "General");
_maPeriod = Param(nameof(MaPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period", "Indicator");
_psarStep = Param(nameof(PsarStep), 0.02m)
.SetGreaterThanZero()
.SetDisplay("PSAR Step", "Parabolic SAR step", "Indicator");
_psarMax = Param(nameof(PsarMax), 0.2m)
.SetGreaterThanZero()
.SetDisplay("PSAR Max", "Parabolic SAR maximum", "Indicator");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Bars between entries", "General");
_closeType = Param(nameof(CloseType), CloseTypes.SlClose)
.SetDisplay("Close Type", "Use stops or trend reversals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candles for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_prevClose = 0m;
_prevSma = 0m;
_tick = 0m;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_tick = Security?.PriceStep ?? 1m;
_barsSinceTrade = CooldownBars;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
_barsSinceTrade++;
var upSignal = _prevClose != 0m && _prevSma != 0m && _prevClose <= _prevSma && candle.ClosePrice > smaValue;
var downSignal = _prevClose != 0m && _prevSma != 0m && _prevClose >= _prevSma && candle.ClosePrice < smaValue;
if (Position > 0)
{
if (CloseType == CloseTypes.TrendClose && downSignal)
{
SellMarket(Position);
_barsSinceTrade = 0;
}
if (TakeProfit > 0 && candle.HighPrice >= _takePrice)
{
SellMarket(Position);
_barsSinceTrade = 0;
}
if (StopLoss > 0 && candle.LowPrice <= _stopPrice)
{
SellMarket(Position);
_barsSinceTrade = 0;
}
if (TrailingStop > 0)
{
var trail = candle.ClosePrice - TrailingStop * _tick;
if (trail > _stopPrice)
_stopPrice = trail;
}
if (Averaging && upSignal && _barsSinceTrade >= CooldownBars)
EnterLong(candle);
}
else if (Position < 0)
{
if (CloseType == CloseTypes.TrendClose && upSignal)
{
BuyMarket(Math.Abs(Position));
_barsSinceTrade = 0;
}
if (TakeProfit > 0 && candle.LowPrice <= _takePrice)
{
BuyMarket(Math.Abs(Position));
_barsSinceTrade = 0;
}
if (StopLoss > 0 && candle.HighPrice >= _stopPrice)
{
BuyMarket(Math.Abs(Position));
_barsSinceTrade = 0;
}
if (TrailingStop > 0)
{
var trail = candle.ClosePrice + TrailingStop * _tick;
if (trail < _stopPrice)
_stopPrice = trail;
}
if (Averaging && downSignal && _barsSinceTrade >= CooldownBars)
EnterShort(candle);
}
else
{
if (upSignal && _barsSinceTrade >= CooldownBars)
EnterLong(candle);
else if (downSignal && _barsSinceTrade >= CooldownBars)
EnterShort(candle);
}
_prevClose = candle.ClosePrice;
_prevSma = smaValue;
}
private void EnterLong(ICandleMessage candle)
{
BuyMarket(Volume);
_entryPrice = candle.ClosePrice;
_stopPrice = StopLoss > 0 ? _entryPrice - StopLoss * _tick : 0m;
_takePrice = TakeProfit > 0 ? _entryPrice + TakeProfit * _tick : 0m;
_barsSinceTrade = 0;
}
private void EnterShort(ICandleMessage candle)
{
SellMarket(Volume);
_entryPrice = candle.ClosePrice;
_stopPrice = StopLoss > 0 ? _entryPrice + StopLoss * _tick : 0m;
_takePrice = TakeProfit > 0 ? _entryPrice - TakeProfit * _tick : 0m;
_barsSinceTrade = 0;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
# CloseTypes: 0 = SlClose, 1 = TrendClose
class ro_nz_rapid_fire_strategy(Strategy):
def __init__(self):
super(ro_nz_rapid_fire_strategy, self).__init__()
self._stop_loss = self.Param("StopLoss", 150) \
.SetDisplay("Stop Loss", "Stop loss in ticks", "Risk")
self._take_profit = self.Param("TakeProfit", 100) \
.SetDisplay("Take Profit", "Take profit in ticks", "Risk")
self._trailing_stop = self.Param("TrailingStop", 0) \
.SetDisplay("Trailing Stop", "Trailing stop in ticks", "Risk")
self._averaging = self.Param("Averaging", False) \
.SetDisplay("Averaging", "Add to position on continuing trend", "General")
self._ma_period = self.Param("MaPeriod", 30) \
.SetDisplay("MA Period", "Moving average period", "Indicator")
self._psar_step = self.Param("PsarStep", 0.02) \
.SetDisplay("PSAR Step", "Parabolic SAR step", "Indicator")
self._psar_max = self.Param("PsarMax", 0.2) \
.SetDisplay("PSAR Max", "Parabolic SAR maximum", "Indicator")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Bars between entries", "General")
self._close_type = self.Param("CloseType", 0) \
.SetDisplay("Close Type", "0=StopClose, 1=TrendClose", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candles for calculations", "General")
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._prev_close = 0.0
self._prev_sma = 0.0
self._tick = 0.0
self._bars_since_trade = 0
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def TrailingStop(self):
return self._trailing_stop.Value
@TrailingStop.setter
def TrailingStop(self, value):
self._trailing_stop.Value = value
@property
def Averaging(self):
return self._averaging.Value
@Averaging.setter
def Averaging(self, value):
self._averaging.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
@property
def PsarStep(self):
return self._psar_step.Value
@PsarStep.setter
def PsarStep(self, value):
self._psar_step.Value = value
@property
def PsarMax(self):
return self._psar_max.Value
@PsarMax.setter
def PsarMax(self, value):
self._psar_max.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def CloseType(self):
return self._close_type.Value
@CloseType.setter
def CloseType(self, value):
self._close_type.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(ro_nz_rapid_fire_strategy, self).OnStarted2(time)
sec = self.Security
self._tick = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 1.0
self._bars_since_trade = self.CooldownBars
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(sma, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
self._bars_since_trade += 1
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
sma_val = float(sma_value)
up_signal = (self._prev_close != 0.0 and self._prev_sma != 0.0
and self._prev_close <= self._prev_sma and close > sma_val)
down_signal = (self._prev_close != 0.0 and self._prev_sma != 0.0
and self._prev_close >= self._prev_sma and close < sma_val)
pos = self.Position
if pos > 0:
if self.CloseType == 1 and down_signal:
self.SellMarket(pos)
self._bars_since_trade = 0
if self.TakeProfit > 0 and high >= self._take_price:
self.SellMarket(self.Position)
self._bars_since_trade = 0
if self.StopLoss > 0 and low <= self._stop_price:
self.SellMarket(self.Position)
self._bars_since_trade = 0
if self.TrailingStop > 0:
trail = close - self.TrailingStop * self._tick
if trail > self._stop_price:
self._stop_price = trail
if self.Averaging and up_signal and self._bars_since_trade >= self.CooldownBars:
self._enter_long(candle)
elif pos < 0:
if self.CloseType == 1 and up_signal:
self.BuyMarket(abs(pos))
self._bars_since_trade = 0
if self.TakeProfit > 0 and low <= self._take_price:
self.BuyMarket(abs(self.Position))
self._bars_since_trade = 0
if self.StopLoss > 0 and high >= self._stop_price:
self.BuyMarket(abs(self.Position))
self._bars_since_trade = 0
if self.TrailingStop > 0:
trail = close + self.TrailingStop * self._tick
if trail < self._stop_price:
self._stop_price = trail
if self.Averaging and down_signal and self._bars_since_trade >= self.CooldownBars:
self._enter_short(candle)
else:
if up_signal and self._bars_since_trade >= self.CooldownBars:
self._enter_long(candle)
elif down_signal and self._bars_since_trade >= self.CooldownBars:
self._enter_short(candle)
self._prev_close = close
self._prev_sma = sma_val
def _enter_long(self, candle):
self.BuyMarket(self.Volume)
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._entry_price - self.StopLoss * self._tick if self.StopLoss > 0 else 0.0
self._take_price = self._entry_price + self.TakeProfit * self._tick if self.TakeProfit > 0 else 0.0
self._bars_since_trade = 0
def _enter_short(self, candle):
self.SellMarket(self.Volume)
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._entry_price + self.StopLoss * self._tick if self.StopLoss > 0 else 0.0
self._take_price = self._entry_price - self.TakeProfit * self._tick if self.TakeProfit > 0 else 0.0
self._bars_since_trade = 0
def OnReseted(self):
super(ro_nz_rapid_fire_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._prev_close = 0.0
self._prev_sma = 0.0
self._tick = 0.0
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return ro_nz_rapid_fire_strategy()