MA Stochastic Strategie
MA Stochastic verwendet einen gleitenden Durchschnitt als Trendfilter mit Rücksetzern des Stochastik-Oszillators. Wenn der Kurs über dem Durchschnitt aufwärts tendiert und der Stochastik in die überverkaufte Zone taucht, bereitet das System den Kauf beim nächsten Aufschwung vor.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 151%. Die Strategie funktioniert am besten auf dem Aktienmarkt.
Short-Trades spiegeln diese Logik für Abwärtstrends wider: Rallyes werden verkauft, wenn der Stochastik Überkauft-Niveaus erreicht.
Feste prozentuale Stops helfen, große Verluste bei plötzlicher Trendumkehr zu vermeiden.
Details
- Einstiegskriterien: Indikatorsignal
- Long/Short: Beide
- Ausstiegskriterien: Stop-Loss oder entgegengesetztes Signal
- Stops: Ja, prozentbasiert
- Standardwerte:
CandleType= 15 minuteStopLoss= 2%
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Moving Average, Stochastic
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Strategy that combines Moving Average and manual Stochastic %K calculation.
/// Enters when price is above MA and Stochastic oversold (longs)
/// or below MA and Stochastic overbought (shorts).
/// </summary>
public class MaStochasticStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<int> _cooldownBars;
private int _cooldown;
private readonly List<decimal> _highs = new();
private readonly List<decimal> _lows = new();
private readonly List<decimal> _closes = new();
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Moving Average period.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Stochastic period for %K calculation.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Stochastic oversold level.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// Stochastic overbought level.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Strategy constructor.
/// </summary>
public MaStochasticStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetRange(10, 50)
.SetDisplay("MA Period", "Period of the Moving Average", "Indicators");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetRange(5, 30)
.SetDisplay("Stochastic Period", "Period for %K calculation", "Indicators");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Stochastic Oversold", "Level considered oversold", "Indicators");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Stochastic Overbought", "Level considered overbought", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 100)
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
.SetRange(5, 500);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cooldown = 0;
_highs.Clear();
_lows.Clear();
_closes.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Track highs, lows, closes for manual stochastic
_highs.Add(candle.HighPrice);
_lows.Add(candle.LowPrice);
_closes.Add(candle.ClosePrice);
// Keep buffers manageable
var maxBuf = StochPeriod * 2;
if (_highs.Count > maxBuf)
{
_highs.RemoveRange(0, _highs.Count - maxBuf);
_lows.RemoveRange(0, _lows.Count - maxBuf);
_closes.RemoveRange(0, _closes.Count - maxBuf);
}
if (_highs.Count < StochPeriod)
return;
// Calculate %K manually
var start = _highs.Count - StochPeriod;
var highestHigh = decimal.MinValue;
var lowestLow = decimal.MaxValue;
for (var i = start; i < _highs.Count; i++)
{
if (_highs[i] > highestHigh) highestHigh = _highs[i];
if (_lows[i] < lowestLow) lowestLow = _lows[i];
}
var diff = highestHigh - lowestLow;
if (diff == 0)
return;
var stochK = 100m * (candle.ClosePrice - lowestLow) / diff;
var close = candle.ClosePrice;
if (_cooldown > 0)
{
_cooldown--;
return;
}
// Long: price above MA + Stochastic oversold
if (close > maValue && stochK < StochOversold && Position == 0)
{
BuyMarket();
_cooldown = CooldownBars;
}
// Short: price below MA + Stochastic overbought
else if (close < maValue && stochK > StochOverbought && Position == 0)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit long: price below MA
if (Position > 0 && close < maValue)
{
SellMarket();
_cooldown = CooldownBars;
}
// Exit short: price above MA
else if (Position < 0 && close > maValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_stochastic_strategy(Strategy):
"""
MA + manual Stochastic strategy.
Enters when price is above MA and Stochastic oversold (longs)
or below MA and Stochastic overbought (shorts).
"""
def __init__(self):
super(ma_stochastic_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._ma_period = self.Param("MaPeriod", 20).SetDisplay("MA Period", "Period of the Moving Average", "Indicators")
self._stoch_period = self.Param("StochPeriod", 14).SetDisplay("Stochastic Period", "Period for %K calculation", "Indicators")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Stochastic Oversold", "Level considered oversold", "Indicators")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Stochastic Overbought", "Level considered overbought", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 100).SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._cooldown = 0
self._highs = []
self._lows = []
self._closes = []
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_stochastic_strategy, self).OnReseted()
self._cooldown = 0
self._highs = []
self._lows = []
self._closes = []
def OnStarted2(self, time):
super(ma_stochastic_strategy, self).OnStarted2(time)
self._cooldown = 0
self._highs = []
self._lows = []
self._closes = []
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ma_val):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
ma = float(ma_val)
cd = self._cooldown_bars.Value
sp = self._stoch_period.Value
oversold = float(self._stoch_oversold.Value)
overbought = float(self._stoch_overbought.Value)
# Track highs, lows, closes for manual stochastic
self._highs.append(high)
self._lows.append(low)
self._closes.append(close)
# Keep buffers manageable
max_buf = sp * 2
if len(self._highs) > max_buf:
self._highs = self._highs[-max_buf:]
self._lows = self._lows[-max_buf:]
self._closes = self._closes[-max_buf:]
if len(self._highs) < sp:
return
# Calculate %K manually
recent_h = self._highs[-sp:]
recent_l = self._lows[-sp:]
hh = max(recent_h)
ll = min(recent_l)
diff = hh - ll
if diff == 0:
return
stoch_k = 100.0 * (close - ll) / diff
if self._cooldown > 0:
self._cooldown -= 1
return
# Long: price above MA + Stochastic oversold
if close > ma and stoch_k < oversold and self.Position == 0:
self.BuyMarket()
self._cooldown = cd
# Short: price below MA + Stochastic overbought
elif close < ma and stoch_k > overbought and self.Position == 0:
self.SellMarket()
self._cooldown = cd
# Exit long: price below MA
if self.Position > 0 and close < ma:
self.SellMarket()
self._cooldown = cd
# Exit short: price above MA
elif self.Position < 0 and close > ma:
self.BuyMarket()
self._cooldown = cd
def CreateClone(self):
return ma_stochastic_strategy()