Ver en GitHub

Stochastic Automated Strategy

This strategy trades using the Stochastic Oscillator on the selected candle timeframe. It waits for %K and %D to enter extreme zones and then acts on crossovers to open positions. Fixed take profit and stop loss protect each trade, while a trailing stop locks in profits.

Logic

  1. Entry
    • Long:
      • Both %K and %D are below OverSold two candles ago.
      • %D was above %K two candles ago and below %K one candle ago.
      • %D is rising.
    • Short:
      • Both %K and %D are above OverBought two candles ago.
      • %D was below %K two candles ago and above %K one candle ago.
      • %D is falling.
  2. Exit
    • Position is closed when Stochastic leaves the extreme zone or %D turns in the opposite direction.
    • A trailing stop exits if price retraces by TrailingStop.
    • Global TakeProfit and StopLoss are applied to every trade.

Parameters

Name Description
CandleType Time frame for Stochastic calculations.
KPeriod Lookback period for %K line.
DPeriod Smoothing period for %D line.
Slowing Additional smoothing for %K.
OverBought Upper threshold indicating overbought market.
OverSold Lower threshold indicating oversold market.
TakeProfit Distance from entry for profit target (price units).
StopLoss Distance from entry for protective stop (price units).
TrailingStop Trailing distance once the trade moves in profit (price units).

Indicators

  • StochasticOscillator

Notes

  • Comments in code are in English.
  • Python version is intentionally omitted.
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>
/// Stochastic oscillator based strategy.
/// Buys on K/D crossover in oversold, sells on crossover in overbought.
/// </summary>
public class StochasticAutomatedStrategy : Strategy
{
	private readonly StrategyParam<int> _kPeriod;
	private readonly StrategyParam<int> _dPeriod;
	private readonly StrategyParam<decimal> _overBought;
	private readonly StrategyParam<decimal> _overSold;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _prevK;
	private decimal? _prevD;

	public int KPeriod { get => _kPeriod.Value; set => _kPeriod.Value = value; }
	public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
	public decimal OverBought { get => _overBought.Value; set => _overBought.Value = value; }
	public decimal OverSold { get => _overSold.Value; set => _overSold.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public StochasticAutomatedStrategy()
	{
		_kPeriod = Param(nameof(KPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("%K Period", "Stochastic %K period", "Stochastic");

		_dPeriod = Param(nameof(DPeriod), 3)
			.SetGreaterThanZero()
			.SetDisplay("%D Period", "Stochastic %D period", "Stochastic");

		_overBought = Param(nameof(OverBought), 80m)
			.SetDisplay("Overbought", "Overbought threshold", "Stochastic");

		_overSold = Param(nameof(OverSold), 20m)
			.SetDisplay("Oversold", "Oversold threshold", "Stochastic");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Time frame", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevK = _prevD = null;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var stochastic = new StochasticOscillator();
		stochastic.K.Length = KPeriod;
		stochastic.D.Length = DPeriod;

		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(stochastic, ProcessCandle).Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, stochastic);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var stoch = (IStochasticOscillatorValue)stochValue;
		if (stoch.K is not decimal k || stoch.D is not decimal d)
			return;

		if (_prevK is decimal pk && _prevD is decimal pd)
		{
			// Buy: K crosses above D in oversold zone
			if (pk <= pd && k > d && pd < OverSold && Position <= 0)
				BuyMarket();

			// Sell: K crosses below D in overbought zone
			if (pk >= pd && k < d && pd > OverBought && Position >= 0)
				SellMarket();
		}

		_prevK = k;
		_prevD = d;
	}
}