Intraday Beta Strategy
This strategy searches for intraday turning points using smoothed moving average slopes and the Relative Strength Index (RSI). A long position is opened when the slope of the 10-period moving average turns upward after a downward move, the RSI is below 70, and the previous candle is bullish. A short position is opened when the slope turns downward after an upward move, the RSI is above 30, and the previous candle is bearish.
An Average True Range (ATR) filter blocks new entries when volatility is too high. Open positions are protected by an adaptive trailing stop that moves in the trade's favor and exits when price crosses the stop level.
Parameters
- RSI Period – period of the RSI indicator.
- Trailing Stop – trailing stop distance in price units.
- ATR Threshold – maximum ATR value allowed for trading.
- Candle Type – timeframe of candles used for analysis.
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>
/// Intraday strategy based on SMA slope changes and RSI with ATR trailing stop.
/// </summary>
public class IntradayBetaStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMa10;
private decimal _prevSlope;
private decimal _prevCandleDiff;
private decimal _longStop;
private decimal _shortStop;
private decimal _entryPrice;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public IntradayBetaStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevMa10 = 0; _prevSlope = 0; _prevCandleDiff = 0;
_longStop = 0; _shortStop = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma10 = new SimpleMovingAverage { Length = 10 };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType).Bind(ma10, rsi, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ma10Value, decimal rsiValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished) return;
if (_prevMa10 == 0) { _prevMa10 = ma10Value; return; }
var ma10Slope = ma10Value - _prevMa10;
var candleDiff = candle.ClosePrice - candle.OpenPrice;
var trailDist = atrValue > 0 ? atrValue * 2 : 100;
var sellSignal = ma10Slope < 0 && _prevSlope > 0 && rsiValue >= 30 && _prevCandleDiff < 0;
var buySignal = ma10Slope > 0 && _prevSlope < 0 && rsiValue <= 70 && _prevCandleDiff > 0;
if (sellSignal && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_entryPrice = candle.ClosePrice;
_shortStop = _entryPrice + trailDist;
}
else if (buySignal && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_entryPrice = candle.ClosePrice;
_longStop = _entryPrice - trailDist;
}
if (Position > 0)
{
var newStop = candle.ClosePrice - trailDist;
if (newStop > _longStop && candle.ClosePrice > _entryPrice)
_longStop = newStop;
if (candle.LowPrice <= _longStop) SellMarket();
}
else if (Position < 0)
{
var newStop = candle.ClosePrice + trailDist;
if (newStop < _shortStop && candle.ClosePrice < _entryPrice)
_shortStop = newStop;
if (candle.HighPrice >= _shortStop) BuyMarket();
}
_prevMa10 = ma10Value;
_prevSlope = ma10Slope;
_prevCandleDiff = candleDiff;
}
}
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 RelativeStrengthIndex, SimpleMovingAverage, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class intraday_beta_strategy(Strategy):
def __init__(self):
super(intraday_beta_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 9) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for trailing", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_ma10 = 0.0
self._prev_slope = 0.0
self._prev_candle_diff = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
self._entry_price = 0.0
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(intraday_beta_strategy, self).OnReseted()
self._prev_ma10 = 0.0
self._prev_slope = 0.0
self._prev_candle_diff = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(intraday_beta_strategy, self).OnStarted2(time)
ma10 = SimpleMovingAverage()
ma10.Length = 10
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma10, rsi, atr, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ma10_value, rsi_value, atr_value):
if candle.State != CandleStates.Finished:
return
if self._prev_ma10 == 0:
self._prev_ma10 = ma10_value
return
ma10_slope = ma10_value - self._prev_ma10
candle_diff = candle.ClosePrice - candle.OpenPrice
trail_dist = atr_value * 2 if atr_value > 0 else 100
sell_signal = ma10_slope < 0 and self._prev_slope > 0 and rsi_value >= 30 and self._prev_candle_diff < 0
buy_signal = ma10_slope > 0 and self._prev_slope < 0 and rsi_value <= 70 and self._prev_candle_diff > 0
if sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = candle.ClosePrice
self._short_stop = self._entry_price + trail_dist
elif buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = candle.ClosePrice
self._long_stop = self._entry_price - trail_dist
if self.Position > 0:
new_stop = candle.ClosePrice - trail_dist
if new_stop > self._long_stop and candle.ClosePrice > self._entry_price:
self._long_stop = new_stop
if candle.LowPrice <= self._long_stop:
self.SellMarket()
elif self.Position < 0:
new_stop = candle.ClosePrice + trail_dist
if new_stop < self._short_stop and candle.ClosePrice < self._entry_price:
self._short_stop = new_stop
if candle.HighPrice >= self._short_stop:
self.BuyMarket()
self._prev_ma10 = ma10_value
self._prev_slope = ma10_slope
self._prev_candle_diff = candle_diff
def CreateClone(self):
return intraday_beta_strategy()