Strategy using Stochastic oscillator and Williams %R to capture extreme oversold and overbought conditions.
Overview
The strategy monitors two momentum indicators:
Stochastic Oscillator with base length 100 and smoothing 3/3.
Williams %R with length 100.
A long position is opened when the Stochastic %D value drops below 3 while Williams %R is under -99.9, indicating an oversold market.
A short position is opened when Stochastic %D rises above 97 and Williams %R climbs above -0.1, signalling an overbought market.
After entering a trade, the algorithm manages risk through stop loss, take profit, trailing stop and optional break-even movement.
Parameters
StochasticLength – period for Stochastic and Williams %R calculations.
StopLoss – distance from entry price for stop loss in points.
TakeProfit – take profit distance in points.
TrailingStop – activation distance for trailing in points.
TrailingStep – step of trailing stop in points.
BreakEven – profit in points at which the stop is moved to entry.
CandleType – candle time frame for strategy calculations.
Indicators
StochasticOscillator
WilliamsR
Trading Rules
Buy when %D < 3 and Williams %R < -99.9.
Sell when %D > 97 and Williams %R > -0.1.
After entry, apply stop loss and take profit.
Move stop to break-even when price advances by BreakEven.
Activate trailing stop after price moves by TrailingStop, shifting by TrailingStep.
Notes
The strategy uses the high-level API of StockSharp and is intended as an educational example.
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>
/// Strategy entering on extreme Stochastic and Williams %R values.
/// </summary>
public class TheMasterMindStrategy : Strategy
{
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _wpr;
private decimal? _prevD;
private int _lastSignal;
/// <summary>
/// Stochastic base length.
/// </summary>
public int StochasticLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// Candle type for strategy calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="TheMasterMindStrategy"/>.
/// </summary>
public TheMasterMindStrategy()
{
_stochLength = Param(nameof(StochasticLength), 14)
.SetDisplay("Stochastic Length", "Base length for Stochastic", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle type for calculations", "Common");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevD = null;
_lastSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stochastic = new StochasticOscillator
{
K = { Length = StochasticLength },
D = { Length = 3 }
};
_wpr = new WilliamsR { Length = StochasticLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stochastic, (candle, stochValue) =>
{
if (candle.State != CandleStates.Finished)
return;
// Process WilliamsR manually (candle-based)
var wprResult = _wpr.Process(candle);
if (!_wpr.IsFormed)
return;
var stoch = (IStochasticOscillatorValue)stochValue;
if (stoch.D is not decimal d || stoch.K is not decimal k)
return;
var wpr = wprResult.ToDecimal();
var buySignal = _prevD >= 20m && d < 20m && wpr < -85m;
var sellSignal = _prevD <= 80m && d > 80m && wpr > -15m;
if (buySignal && _lastSignal != 1 && Position <= 0)
{
BuyMarket();
_lastSignal = 1;
}
else if (sellSignal && _lastSignal != -1 && Position >= 0)
{
SellMarket();
_lastSignal = -1;
}
_prevD = d;
})
.Start();
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import StochasticOscillator, WilliamsR, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class the_master_mind_strategy(Strategy):
def __init__(self):
super(the_master_mind_strategy, self).__init__()
self._stoch_length = self.Param("StochasticLength", 14)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._wpr = None
self._prev_d = None
self._last_signal = 0
@property
def StochasticLength(self):
return self._stoch_length.Value
@StochasticLength.setter
def StochasticLength(self, value):
self._stoch_length.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(the_master_mind_strategy, self).OnStarted2(time)
self._prev_d = None
self._last_signal = 0
self._wpr = WilliamsR()
self._wpr.Length = self.StochasticLength
stoch = StochasticOscillator()
stoch.K.Length = self.StochasticLength
stoch.D.Length = 3
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stoch, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
wpr_input = CandleIndicatorValue(self._wpr, candle)
wpr_input.IsFinal = True
wpr_result = self._wpr.Process(wpr_input)
if not self._wpr.IsFormed:
return
wpr = float(wpr_result)
d = float(stoch_value.D)
k = float(stoch_value.K)
if self._prev_d is not None:
buy_signal = self._prev_d >= 20.0 and d < 20.0 and wpr < -85.0
sell_signal = self._prev_d <= 80.0 and d > 80.0 and wpr > -15.0
if buy_signal and self.Position <= 0:
self.BuyMarket()
self._last_signal = 1
elif sell_signal and self.Position >= 0:
self.SellMarket()
self._last_signal = -1
self._prev_d = d
def OnReseted(self):
super(the_master_mind_strategy, self).OnReseted()
self._wpr = None
self._prev_d = None
self._last_signal = 0
def CreateClone(self):
return the_master_mind_strategy()