MasterMind 2-Strategie
Überblick
MasterMind 2 ist eine Konvertierung des Expertenberaters „TheMasterMind2“ MQL4. Die Strategie wartet auf Extremwerte der Indikatoren Stochastic Oscillator und Williams %R, um Erschöpfungspunkte zu erkennen. Wenn beide Indikatoren extrem überverkaufte Bedingungen anzeigen, wird eine Long-Position eröffnet, und wenn beide Indikatoren extrem überkaufte Bedingungen anzeigen, wird eine Short-Position eröffnet. Die Logik funktioniert nur bei vollständig geschlossenen Kerzen und ahmt das ursprüngliche Verhalten des Expert Advisors nach.
Indikatoren
- Stochastic Oszillator – konfiguriert mit einem langen Lookback, um überkaufte und überverkaufte Niveaus zu messen. Die %D-Signalleitung wird mit Schwellenwerten verglichen.
- Williams %R – bestätigt die Stärke des Extrems, indem es Werte nahe -100 für Long-Positionen und nahe 0 für Short-Positionen erfordert.
Teilnahmebedingungen
- Warten Sie, bis sich eine Kerze schließt.
- Berechnen Sie den Stochastic-Oszillator und nehmen Sie seinen %D-Signalwert.
- Berechnen Sie Williams %R über den konfigurierten Lookback.
- Long-Einstieg: Wenn
%D < 3undWilliams %R < -99.9, schließen Sie alle bestehenden Short-Positionen und kaufen Sie. - Short-Einstieg: Bei
%D > 97undWilliams %R > -0.1alle bestehenden Long-Engagements schließen und verkaufen.
Ausgangsregeln
- Stop-Loss- und Take-Profit-Level werden relativ zum Einstiegspreis mithilfe konfigurierbarer Punktabstände angewendet.
- Ein Trailing-Stop kann den Schutzstopp verschärfen, sobald sich der Preis um die angegebene Stufe günstig bewegt.
- Eine Break-Even-Option verschiebt den Stop-Loss auf das Einstiegsniveau, nachdem der Handel die erforderliche Gewinndistanz erreicht hat.
- Entgegengesetzte Signale schließen sofort die aktuelle Position, bevor sie eine neue eröffnen.
Parameter
Trade Volume– Vertragsvolumen, das mit jeder Marktorder übermittelt wird.Stochastic Period,Stochastic %K,Stochastic %D– Parameter des Stochastic-Oszillators.Williams %R Period– Lookback-Zeitraum für die Williams %R-Berechnung.Stop Loss,Take Profit– Schutzabstände in Preispunkten.Trailing Stop,Trailing Step– dynamische Stoppverwaltung steuern.Break Even– Distanz in Punkten, die erforderlich ist, um den Einstiegspreis zu sichern.Candle Type– Zeitrahmen oder benutzerdefinierter Kerzentyp, der in Berechnungen verwendet wird.
Notizen
- Die Strategie basiert ausschließlich auf fertigen Kerzen, passend zur ursprünglichen MQL4-Implementierung.
- Alle Aufträge werden zum Marktwert mit einem durch
Trade Volumedefinierten Volumen erteilt. - Aktivieren oder deaktivieren Sie die Schutzfunktionen, indem Sie die Abstandsparameter auf Null setzen.
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>
/// MasterMind 2 strategy converted from MQL4 implementation.
/// Uses Stochastic Oscillator and Williams %R to detect extreme conditions
/// and complements signals with stop management rules.
/// </summary>
public class MasterMind2Strategy : Strategy
{
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<int> _stochasticPeriod;
private readonly StrategyParam<int> _stochasticK;
private readonly StrategyParam<int> _stochasticD;
private readonly StrategyParam<int> _williamsPeriod;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<decimal> _trailingStepPoints;
private readonly StrategyParam<decimal> _breakEvenPoints;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takeProfitPrice;
/// <summary>
/// Trade volume in contracts.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Period for the Stochastic Oscillator.
/// </summary>
public int StochasticPeriod
{
get => _stochasticPeriod.Value;
set => _stochasticPeriod.Value = value;
}
/// <summary>
/// Smoothing length for the %K line.
/// </summary>
public int StochasticK
{
get => _stochasticK.Value;
set => _stochasticK.Value = value;
}
/// <summary>
/// Smoothing length for the %D signal line.
/// </summary>
public int StochasticD
{
get => _stochasticD.Value;
set => _stochasticD.Value = value;
}
/// <summary>
/// Period for Williams %R.
/// </summary>
public int WilliamsPeriod
{
get => _williamsPeriod.Value;
set => _williamsPeriod.Value = value;
}
/// <summary>
/// Stop loss distance expressed in price points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit distance expressed in price points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Trailing stop distance expressed in price points.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Minimum price improvement required to move the trailing stop.
/// </summary>
public decimal TrailingStepPoints
{
get => _trailingStepPoints.Value;
set => _trailingStepPoints.Value = value;
}
/// <summary>
/// Distance required to move the stop loss to break-even.
/// </summary>
public decimal BreakEvenPoints
{
get => _breakEvenPoints.Value;
set => _breakEvenPoints.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes default parameters.
/// </summary>
public MasterMind2Strategy()
{
_tradeVolume = Param(nameof(TradeVolume), 0.1m)
.SetDisplay("Trade Volume", "Trade volume in contracts", "General");
_stochasticPeriod = Param(nameof(StochasticPeriod), 100)
.SetDisplay("Stochastic Period", "Period for the Stochastic Oscillator", "Indicators")
;
_stochasticK = Param(nameof(StochasticK), 3)
.SetDisplay("Stochastic %K", "Smoothing length for %K", "Indicators")
;
_stochasticD = Param(nameof(StochasticD), 3)
.SetDisplay("Stochastic %D", "Smoothing length for %D", "Indicators")
;
_williamsPeriod = Param(nameof(WilliamsPeriod), 100)
.SetDisplay("Williams %R Period", "Lookback for Williams %R", "Indicators")
;
_stopLossPoints = Param(nameof(StopLossPoints), 2000m)
.SetDisplay("Stop Loss", "Stop loss distance in price points", "Risk")
;
_takeProfitPoints = Param(nameof(TakeProfitPoints), 0m)
.SetDisplay("Take Profit", "Take profit distance in price points", "Risk")
;
_trailingStopPoints = Param(nameof(TrailingStopPoints), 0m)
.SetDisplay("Trailing Stop", "Trailing stop distance in price points", "Risk")
;
_trailingStepPoints = Param(nameof(TrailingStepPoints), 1m)
.SetDisplay("Trailing Step", "Minimum improvement to trail stop", "Risk")
;
_breakEvenPoints = Param(nameof(BreakEvenPoints), 0m)
.SetDisplay("Break Even", "Distance to move stop to break-even", "Risk")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle type used for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security, DataType)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetStops();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochasticPeriod;
stochastic.D.Length = StochasticD;
var williams = new WilliamsR
{
Length = WilliamsPeriod
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stochastic, williams, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, stochastic);
DrawIndicator(area, williams);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochasticValue, IIndicatorValue williamsValue)
{
// Only react to fully formed candles to mirror MQL logic.
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!stochasticValue.IsFinal || !williamsValue.IsFinal)
return;
var stoch = (StochasticOscillatorValue)stochasticValue;
if (stoch.D is not decimal signal)
return;
var wpr = williamsValue.ToDecimal();
var step = Security?.PriceStep ?? 1m;
if (step <= 0m)
step = 1m;
ManageLongPosition(candle, step);
ManageShortPosition(candle, step);
// Generate entries only when no opposite position exists.
if (signal < 5m && wpr < -95m)
{
HandleBuySignal(candle, step);
}
else if (signal > 95m && wpr > -5m)
{
HandleSellSignal(candle, step);
}
}
private void ManageLongPosition(ICandleMessage candle, decimal step)
{
if (Position <= 0)
return;
// Move stop to entry once break-even condition is reached.
if (BreakEvenPoints > 0m && candle.ClosePrice - _entryPrice >= BreakEvenPoints * step &&
(_stopPrice == 0m || _stopPrice < _entryPrice))
{
_stopPrice = _entryPrice;
}
// Tighten trailing stop when price moves favorably.
if (TrailingStopPoints > 0m)
{
var candidateStop = candle.ClosePrice - TrailingStopPoints * step;
if (_stopPrice == 0m || candidateStop - _stopPrice >= TrailingStepPoints * step)
{
_stopPrice = candidateStop;
}
}
// Exit position when stop or target is triggered.
var stopHit = _stopPrice > 0m && candle.LowPrice <= _stopPrice;
var targetHit = _takeProfitPrice > 0m && candle.HighPrice >= _takeProfitPrice;
if (stopHit || targetHit)
{
SellMarket(Position);
ResetStops();
}
}
private void ManageShortPosition(ICandleMessage candle, decimal step)
{
if (Position >= 0)
return;
if (BreakEvenPoints > 0m && _entryPrice - candle.ClosePrice >= BreakEvenPoints * step &&
(_stopPrice == 0m || _stopPrice > _entryPrice))
{
_stopPrice = _entryPrice;
}
if (TrailingStopPoints > 0m)
{
var candidateStop = candle.ClosePrice + TrailingStopPoints * step;
if (_stopPrice == 0m || _stopPrice - candidateStop >= TrailingStepPoints * step)
{
_stopPrice = candidateStop;
}
}
var stopHit = _stopPrice > 0m && candle.HighPrice >= _stopPrice;
var targetHit = _takeProfitPrice > 0m && candle.LowPrice <= _takeProfitPrice;
if (stopHit || targetHit)
{
BuyMarket(Math.Abs(Position));
ResetStops();
}
}
private void HandleBuySignal(ICandleMessage candle, decimal step)
{
if (Position < 0)
{
// Close short before opening a long position.
BuyMarket(Math.Abs(Position));
ResetStops();
}
if (Position > 0 || TradeVolume <= 0m)
return;
BuyMarket(TradeVolume);
_entryPrice = candle.ClosePrice;
_stopPrice = StopLossPoints > 0m ? _entryPrice - StopLossPoints * step : 0m;
_takeProfitPrice = TakeProfitPoints > 0m ? _entryPrice + TakeProfitPoints * step : 0m;
}
private void HandleSellSignal(ICandleMessage candle, decimal step)
{
if (Position > 0)
{
// Close long before opening a short position.
SellMarket(Position);
ResetStops();
}
if (Position < 0 || TradeVolume <= 0m)
return;
SellMarket(TradeVolume);
_entryPrice = candle.ClosePrice;
_stopPrice = StopLossPoints > 0m ? _entryPrice + StopLossPoints * step : 0m;
_takeProfitPrice = TakeProfitPoints > 0m ? _entryPrice - TakeProfitPoints * step : 0m;
}
private void ResetStops()
{
_entryPrice = 0m;
_stopPrice = 0m;
_takeProfitPrice = 0m;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import StochasticOscillator, WilliamsR
class master_mind2_strategy(Strategy):
def __init__(self):
super(master_mind2_strategy, self).__init__()
self._trade_volume = self.Param("TradeVolume", 0.1) \
.SetDisplay("Trade Volume", "Trade volume in contracts", "General")
self._stochastic_period = self.Param("StochasticPeriod", 100) \
.SetDisplay("Stochastic Period", "Period for the Stochastic Oscillator", "Indicators")
self._stochastic_k = self.Param("StochasticK", 3) \
.SetDisplay("Stochastic %K", "Smoothing length for %K", "Indicators")
self._stochastic_d = self.Param("StochasticD", 3) \
.SetDisplay("Stochastic %D", "Smoothing length for %D", "Indicators")
self._williams_period = self.Param("WilliamsPeriod", 100) \
.SetDisplay("Williams %R Period", "Lookback for Williams %R", "Indicators")
self._stop_loss_points = self.Param("StopLossPoints", 2000.0) \
.SetDisplay("Stop Loss", "Stop loss distance in price points", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 0.0) \
.SetDisplay("Take Profit", "Take profit distance in price points", "Risk")
self._trailing_stop_points = self.Param("TrailingStopPoints", 0.0) \
.SetDisplay("Trailing Stop", "Trailing stop distance in price points", "Risk")
self._trailing_step_points = self.Param("TrailingStepPoints", 1.0) \
.SetDisplay("Trailing Step", "Minimum improvement to trail stop", "Risk")
self._break_even_points = self.Param("BreakEvenPoints", 0.0) \
.SetDisplay("Break Even", "Distance to move stop to break-even", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle type used for calculations", "General")
self._entry_price = 0.0
self._stop_price = 0.0
self._take_profit_price = 0.0
@property
def TradeVolume(self):
return self._trade_volume.Value
@property
def StochasticPeriod(self):
return self._stochastic_period.Value
@property
def StochasticK(self):
return self._stochastic_k.Value
@property
def StochasticD(self):
return self._stochastic_d.Value
@property
def WilliamsPeriod(self):
return self._williams_period.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@property
def TrailingStepPoints(self):
return self._trailing_step_points.Value
@property
def BreakEvenPoints(self):
return self._break_even_points.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(master_mind2_strategy, self).OnStarted2(time)
stochastic = StochasticOscillator()
stochastic.K.Length = self.StochasticPeriod
stochastic.D.Length = self.StochasticD
williams = WilliamsR()
williams.Length = self.WilliamsPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stochastic, williams, self.ProcessCandle).Start()
def ProcessCandle(self, candle, stochastic_value, williams_value):
if candle.State != CandleStates.Finished:
return
if not stochastic_value.IsFinal or not williams_value.IsFinal:
return
stoch = stochastic_value
signal = stoch.D
if signal is None:
return
signal = float(signal)
wpr = float(williams_value)
ps = self.Security.PriceStep if self.Security is not None else None
step = float(ps) if ps is not None else 1.0
if step <= 0:
step = 1.0
self._manage_long_position(candle, step)
self._manage_short_position(candle, step)
if signal < 5.0 and wpr < -95.0:
self._handle_buy_signal(candle, step)
elif signal > 95.0 and wpr > -5.0:
self._handle_sell_signal(candle, step)
def _manage_long_position(self, candle, step):
if self.Position <= 0:
return
be = float(self.BreakEvenPoints)
if be > 0 and float(candle.ClosePrice) - self._entry_price >= be * step:
if self._stop_price == 0 or self._stop_price < self._entry_price:
self._stop_price = self._entry_price
ts = float(self.TrailingStopPoints)
if ts > 0:
candidate_stop = float(candle.ClosePrice) - ts * step
tst = float(self.TrailingStepPoints)
if self._stop_price == 0 or candidate_stop - self._stop_price >= tst * step:
self._stop_price = candidate_stop
stop_hit = self._stop_price > 0 and float(candle.LowPrice) <= self._stop_price
target_hit = self._take_profit_price > 0 and float(candle.HighPrice) >= self._take_profit_price
if stop_hit or target_hit:
self.SellMarket(self.Position)
self._reset_stops()
def _manage_short_position(self, candle, step):
if self.Position >= 0:
return
be = float(self.BreakEvenPoints)
if be > 0 and self._entry_price - float(candle.ClosePrice) >= be * step:
if self._stop_price == 0 or self._stop_price > self._entry_price:
self._stop_price = self._entry_price
ts = float(self.TrailingStopPoints)
if ts > 0:
candidate_stop = float(candle.ClosePrice) + ts * step
tst = float(self.TrailingStepPoints)
if self._stop_price == 0 or self._stop_price - candidate_stop >= tst * step:
self._stop_price = candidate_stop
stop_hit = self._stop_price > 0 and float(candle.HighPrice) >= self._stop_price
target_hit = self._take_profit_price > 0 and float(candle.LowPrice) <= self._take_profit_price
if stop_hit or target_hit:
self.BuyMarket(abs(self.Position))
self._reset_stops()
def _handle_buy_signal(self, candle, step):
if self.Position < 0:
self.BuyMarket(abs(self.Position))
self._reset_stops()
tv = float(self.TradeVolume)
if self.Position > 0 or tv <= 0:
return
self.BuyMarket(tv)
self._entry_price = float(candle.ClosePrice)
sl = float(self.StopLossPoints)
tp = float(self.TakeProfitPoints)
self._stop_price = self._entry_price - sl * step if sl > 0 else 0.0
self._take_profit_price = self._entry_price + tp * step if tp > 0 else 0.0
def _handle_sell_signal(self, candle, step):
if self.Position > 0:
self.SellMarket(self.Position)
self._reset_stops()
tv = float(self.TradeVolume)
if self.Position < 0 or tv <= 0:
return
self.SellMarket(tv)
self._entry_price = float(candle.ClosePrice)
sl = float(self.StopLossPoints)
tp = float(self.TakeProfitPoints)
self._stop_price = self._entry_price + sl * step if sl > 0 else 0.0
self._take_profit_price = self._entry_price - tp * step if tp > 0 else 0.0
def _reset_stops(self):
self._entry_price = 0.0
self._stop_price = 0.0
self._take_profit_price = 0.0
def OnReseted(self):
super(master_mind2_strategy, self).OnReseted()
self._reset_stops()
def CreateClone(self):
return master_mind2_strategy()