MCOTs Intuition Strategy
Strategy based on RSI momentum relative to its standard deviation. It buys when upward momentum is strong but fading and sells on the opposite. Fixed profit target and stop loss are placed in ticks.
Details
- Entry Criteria:
- Long: momentum > stdDev * multiplier and momentum < previousMomentum * exhaustionMultiplier
- Short: momentum < -stdDev * multiplier and momentum > previousMomentum * exhaustionMultiplier
- Long/Short: Both
- Exit Criteria:
- Fixed profit target and stop loss in ticks
- Stops: Yes
- Default Values:
RsiPeriod= 14StdDevMultiplier= 1mExhaustionMultiplier= 1mProfitTargetTicks= 40StopLossTicks= 160CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filters:
- Category: Reversal
- Direction: Both
- Indicators: RSI, StandardDeviation
- Stops: Yes
- Complexity: Basic
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
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;
public class McotsIntuitionStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _momentumThreshold;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private decimal _prevRsi;
private decimal _prevMomentum;
private decimal _takeProfitPrice;
private decimal _stopLossPrice;
private bool _hasPrev;
private int _barsFromSignal;
private int _barIndex;
private int _entryBar;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public decimal MomentumThreshold { get => _momentumThreshold.Value; set => _momentumThreshold.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public McotsIntuitionStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "General");
_momentumThreshold = Param(nameof(MomentumThreshold), 2m)
.SetGreaterThanZero()
.SetDisplay("Momentum Threshold", "Minimum RSI momentum", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 30)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = null;
_prevRsi = 0m;
_prevMomentum = 0m;
_takeProfitPrice = 0m;
_stopLossPrice = 0m;
_hasPrev = false;
_barsFromSignal = 0;
_barIndex = 0;
_entryBar = -1;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_prevRsi = 0;
_prevMomentum = 0;
_hasPrev = false;
_takeProfitPrice = 0;
_stopLossPrice = 0;
_barsFromSignal = SignalCooldownBars;
_barIndex = 0;
_entryBar = -1;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_rsi.IsFormed)
{
_prevRsi = rsiValue;
return;
}
var momentum = rsiValue - _prevRsi;
_barIndex++;
if (!_hasPrev)
{
_prevRsi = rsiValue;
_prevMomentum = momentum;
_hasPrev = true;
return;
}
_barsFromSignal++;
if (Position == 0)
{
var threshold = MomentumThreshold;
var longSignal = _prevMomentum <= threshold && momentum > threshold && rsiValue >= 50m;
if (_barsFromSignal >= SignalCooldownBars && longSignal)
{
BuyMarket();
_takeProfitPrice = candle.ClosePrice * 1.03m;
_stopLossPrice = candle.ClosePrice * 0.98m;
_barsFromSignal = 0;
_entryBar = _barIndex;
}
}
else if (Position > 0)
{
var timedExit = _entryBar >= 0 && _barIndex - _entryBar >= 16;
if (candle.HighPrice >= _takeProfitPrice || candle.LowPrice <= _stopLossPrice || timedExit)
SellMarket();
}
_prevRsi = rsiValue;
_prevMomentum = momentum;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class mcots_intuition_strategy(Strategy):
def __init__(self):
super(mcots_intuition_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI calculation period", "General")
self._momentum_threshold = self.Param("MomentumThreshold", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Momentum Threshold", "Minimum RSI momentum", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 30) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._prev_rsi = 0.0
self._prev_momentum = 0.0
self._take_profit_price = 0.0
self._stop_loss_price = 0.0
self._has_prev = False
self._bars_from_signal = 0
self._bar_index = 0
self._entry_bar = -1
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(mcots_intuition_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._prev_momentum = 0.0
self._take_profit_price = 0.0
self._stop_loss_price = 0.0
self._has_prev = False
self._bars_from_signal = 0
self._bar_index = 0
self._entry_bar = -1
def OnStarted2(self, time):
super(mcots_intuition_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._prev_momentum = 0.0
self._has_prev = False
self._take_profit_price = 0.0
self._stop_loss_price = 0.0
self._bars_from_signal = self._signal_cooldown_bars.Value
self._bar_index = 0
self._entry_bar = -1
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self._rsi_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._rsi, self.OnProcess).Start()
def OnProcess(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_value)
if not self._rsi.IsFormed:
self._prev_rsi = rv
return
momentum = rv - self._prev_rsi
self._bar_index += 1
if not self._has_prev:
self._prev_rsi = rv
self._prev_momentum = momentum
self._has_prev = True
return
self._bars_from_signal += 1
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
th = float(self._momentum_threshold.Value)
if self.Position == 0:
long_signal = self._prev_momentum <= th and momentum > th and rv >= 50.0
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal >= cd and long_signal:
self.BuyMarket()
self._take_profit_price = close * 1.03
self._stop_loss_price = close * 0.98
self._bars_from_signal = 0
self._entry_bar = self._bar_index
elif self.Position > 0:
timed_exit = self._entry_bar >= 0 and self._bar_index - self._entry_bar >= 16
if high >= self._take_profit_price or low <= self._stop_loss_price or timed_exit:
self.SellMarket()
self._prev_rsi = rv
self._prev_momentum = momentum
def CreateClone(self):
return mcots_intuition_strategy()