Стратегия RoNz Rapid-Fire
Стратегия сочетает скользящую среднюю и индикатор параболического SAR для поиска быстрых смен тренда. Длинная позиция открывается, когда цена закрытия выше скользящей средней и Parabolic SAR переключается ниже цены. Короткая позиция открывается при противоположных условиях. При продолжении тренда возможно усреднение позиции.
Принцип работы
- Вход в лонг: цена закрытия > SMA и Parabolic SAR ниже цены.
- Вход в шорт: цена закрытия < SMA и Parabolic SAR выше цены.
- Выход: по стоп-лоссу/тейк-профиту или по противоположному сигналу в зависимости от режима.
- Усреднение: добавление новых позиций при устойчивом тренде.
- Трейлинг-стоп: смещает уровень стопа при движении цены в прибыль.
Параметры
Volume– объем сделки.StopLoss– стоп-лосс в тиках.TakeProfit– тейк-профит в тиках.TrailingStop– трейлинг-стоп в тиках.Averaging– включить усреднение.MaPeriod– период скользящей средней.PsarStep– шаг Parabolic SAR.PsarMax– максимальное значение Parabolic SAR.CloseType–SlCloseиспользует только стопы,TrendCloseзакрывает при смене тренда.CandleType– тип свечей для расчета.
Примечания
- Работает с любым инструментом, поддерживаемым StockSharp.
- Требуются исторические свечи выбранного типа
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()