MasterMind 2 策略
概述
该策略结合 随机指标 (Stochastic Oscillator) 与 威廉指标 (Williams %R) 用于捕捉极端的超买与超卖区间。 当随机指标信号线低于 3 且威廉指标小于 -99.9 时开多单; 当随机指标信号线高于 97 且威廉指标大于 -0.1 时开空单。
风险控制包括初始止损与止盈、可调步长的移动止损,以及在盈利达到一定水平时将止损移动到开仓价位的保本功能。
参数
LotSize- 交易量。StochasticPeriod- 随机指标周期。StochasticK- %K 线平滑值。StochasticD- %D 线平滑值。WilliamsRPeriod- 威廉指标周期。StopLossPoints- 初始止损点数。TakeProfitPoints- 初始止盈点数。TrailingStopPoints- 移动止损距离。TrailingStepPoints- 移动止损更新所需的最小盈利点数。BreakEvenPoints- 触发保本的点数距离。CandleType- 使用的K线类型及周期。
交易逻辑
- 入场信号
- 随机指标信号线 < 3 且 威廉指标 < -99.9 时买入。
- 随机指标信号线 > 97 且 威廉指标 > -0.1 时卖出。
- 出场信号
- 相反信号平掉当前持仓。
- 每根K线都会检查止损、止盈、保本及移动止损。
备注
- 适用于支持上述指标的任何品种。
- 主要用于学习和进一步研究。
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 based on Stochastic Oscillator and Williams %R.
/// Buys when Stochastic is very low and Williams %R confirms oversold.
/// Sells when Stochastic is very high and Williams %R confirms overbought.
/// Includes trailing stop and break-even logic.
/// </summary>
public class TheMasterMind2Strategy : Strategy
{
private readonly StrategyParam<decimal> _lotSize;
private readonly StrategyParam<int> _stochasticPeriod;
private readonly StrategyParam<int> _stochasticK;
private readonly StrategyParam<int> _stochasticD;
private readonly StrategyParam<int> _wprPeriod;
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;
private decimal _prevSignal = decimal.MinValue;
private decimal _prevWpr = decimal.MinValue;
/// <summary>
/// Trade volume in contracts.
/// </summary>
public decimal LotSize
{
get => _lotSize.Value;
set => _lotSize.Value = value;
}
/// <summary>
/// Period for Stochastic calculation.
/// </summary>
public int StochasticPeriod
{
get => _stochasticPeriod.Value;
set => _stochasticPeriod.Value = value;
}
/// <summary>
/// Smoothing for %K line.
/// </summary>
public int StochasticK
{
get => _stochasticK.Value;
set => _stochasticK.Value = value;
}
/// <summary>
/// Smoothing for %D line.
/// </summary>
public int StochasticD
{
get => _stochasticD.Value;
set => _stochasticD.Value = value;
}
/// <summary>
/// Period for Williams %R.
/// </summary>
public int WilliamsRPeriod
{
get => _wprPeriod.Value;
set => _wprPeriod.Value = value;
}
/// <summary>
/// Initial stop loss in price points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Initial take profit in price points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Trailing stop distance in points.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Trailing step in points.
/// </summary>
public decimal TrailingStepPoints
{
get => _trailingStepPoints.Value;
set => _trailingStepPoints.Value = value;
}
/// <summary>
/// Break-even activation distance in points.
/// </summary>
public decimal BreakEvenPoints
{
get => _breakEvenPoints.Value;
set => _breakEvenPoints.Value = value;
}
/// <summary>
/// Type of candles to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public TheMasterMind2Strategy()
{
_lotSize = Param(nameof(LotSize), 0.1m).SetDisplay("Lot Size", "Trade volume in contracts", "General");
_stochasticPeriod = Param(nameof(StochasticPeriod), 50)
.SetDisplay("Stochastic Period", "Period for Stochastic calculation", "Indicators")
;
_stochasticK = Param(nameof(StochasticK), 3)
.SetDisplay("Stochastic %K", "Smoothing of %K line", "Indicators")
;
_stochasticD = Param(nameof(StochasticD), 3)
.SetDisplay("Stochastic %D", "Smoothing of %D line", "Indicators")
;
_wprPeriod = Param(nameof(WilliamsRPeriod), 50)
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators")
;
_stopLossPoints = Param(nameof(StopLossPoints), 500m)
.SetDisplay("Stop Loss", "Initial stop loss in price points", "Risk")
;
_takeProfitPoints = Param(nameof(TakeProfitPoints), 500m)
.SetDisplay("Take Profit", "Initial take profit in price points", "Risk")
;
_trailingStopPoints = Param(nameof(TrailingStopPoints), 50m)
.SetDisplay("Trailing Stop", "Trailing stop distance in points", "Risk")
;
_trailingStepPoints = Param(nameof(TrailingStepPoints), 100m)
.SetDisplay("Trailing Step", "Minimum move to adjust trailing stop", "Risk")
;
_breakEvenPoints = Param(nameof(BreakEvenPoints), 150m)
.SetDisplay("Break Even", "Move stop to entry after profit", "Risk")
;
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetStops();
_prevSignal = decimal.MinValue;
_prevWpr = decimal.MinValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetStops();
_prevSignal = decimal.MinValue;
_prevWpr = decimal.MinValue;
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochasticPeriod;
stochastic.D.Length = StochasticD;
var williams = new WilliamsR { Length = WilliamsRPeriod };
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 ResetStops()
{
_entryPrice = 0m;
_stopPrice = 0m;
_takeProfitPrice = 0m;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue, IIndicatorValue wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var stochTyped = (StochasticOscillatorValue)stochValue;
if (stochTyped.D is not decimal signal)
return;
var wpr = wprValue.ToDecimal();
var step = Security?.PriceStep ?? 1m;
if (_prevSignal == decimal.MinValue)
{
_prevSignal = signal;
_prevWpr = wpr;
return;
}
// Manage existing position
if (Position > 0)
{
// Activate break-even
if (BreakEvenPoints > 0m && candle.ClosePrice - _entryPrice >= BreakEvenPoints * step &&
(_stopPrice == 0m || _stopPrice < _entryPrice))
_stopPrice = _entryPrice;
// Update trailing stop
if (TrailingStopPoints > 0m)
{
var target = candle.ClosePrice - TrailingStopPoints * step;
if (_stopPrice == 0m || target - _stopPrice >= TrailingStepPoints * step)
_stopPrice = target;
}
// Check stop or take profit
if (candle.LowPrice <= _stopPrice || (_takeProfitPrice > 0m && candle.HighPrice >= _takeProfitPrice))
{
SellMarket();
ResetStops();
}
}
else if (Position < 0)
{
if (BreakEvenPoints > 0m && _entryPrice - candle.ClosePrice >= BreakEvenPoints * step &&
(_stopPrice == 0m || _stopPrice > _entryPrice))
_stopPrice = _entryPrice;
if (TrailingStopPoints > 0m)
{
var target = candle.ClosePrice + TrailingStopPoints * step;
if (_stopPrice == 0m || _stopPrice - target >= TrailingStepPoints * step)
_stopPrice = target;
}
if (candle.HighPrice >= _stopPrice || (_takeProfitPrice > 0m && candle.LowPrice <= _takeProfitPrice))
{
BuyMarket();
ResetStops();
}
}
// Generate trade signals
if (_prevSignal >= 20m && signal < 20m && _prevWpr >= -80m && wpr < -80m && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLossPoints * step;
_takeProfitPrice = _entryPrice + TakeProfitPoints * step;
}
else if (_prevSignal <= 80m && signal > 80m && _prevWpr <= -20m && wpr > -20m && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLossPoints * step;
_takeProfitPrice = _entryPrice - TakeProfitPoints * step;
}
_prevSignal = signal;
_prevWpr = wpr;
}
}
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 StochasticOscillator, WilliamsR
from StockSharp.Algo.Strategies import Strategy
class the_master_mind_2_strategy(Strategy):
"""Stochastic + Williams %R with trailing stop and break-even."""
def __init__(self):
super(the_master_mind_2_strategy, self).__init__()
self._stoch_period = self.Param("StochasticPeriod", 50).SetDisplay("Stochastic Period", "Period for Stochastic", "Indicators")
self._stoch_d = self.Param("StochasticD", 3).SetDisplay("Stochastic %D", "Smoothing of %D line", "Indicators")
self._wpr_period = self.Param("WilliamsRPeriod", 50).SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators")
self._sl_points = self.Param("StopLossPoints", 500.0).SetDisplay("Stop Loss", "Initial SL in price points", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 500.0).SetDisplay("Take Profit", "Initial TP in price points", "Risk")
self._trail_points = self.Param("TrailingStopPoints", 50.0).SetDisplay("Trailing Stop", "Trailing stop distance", "Risk")
self._trail_step = self.Param("TrailingStepPoints", 100.0).SetDisplay("Trailing Step", "Minimum move to adjust trailing", "Risk")
self._be_points = self.Param("BreakEvenPoints", 150.0).SetDisplay("Break Even", "Move stop to entry after profit", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Type of candles", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(the_master_mind_2_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._tp_price = 0.0
self._prev_signal = None
self._prev_wpr = None
def OnStarted2(self, time):
super(the_master_mind_2_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._stop_price = 0.0
self._tp_price = 0.0
self._prev_signal = None
self._prev_wpr = None
stoch = StochasticOscillator()
stoch.K.Length = self._stoch_period.Value
stoch.D.Length = self._stoch_d.Value
wpr = WilliamsR()
wpr.Length = self._wpr_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(stoch, wpr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, stoch)
self.DrawIndicator(area, wpr)
self.DrawOwnTrades(area)
def OnProcess(self, candle, stoch_val, wpr_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
# Extract %D from stochastic
try:
signal = float(stoch_val.D)
except:
return
wpr = float(wpr_val)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
close = float(candle.ClosePrice)
low = float(candle.LowPrice)
high = float(candle.HighPrice)
if self._prev_signal is None:
self._prev_signal = signal
self._prev_wpr = wpr
return
# Manage position
sl_pts = float(self._sl_points.Value)
tp_pts = float(self._tp_points.Value)
trail_pts = float(self._trail_points.Value)
trail_step = float(self._trail_step.Value)
be_pts = float(self._be_points.Value)
if self.Position > 0:
# Break-even
if be_pts > 0 and close - self._entry_price >= be_pts * step and (self._stop_price == 0 or self._stop_price < self._entry_price):
self._stop_price = self._entry_price
# Trailing
if trail_pts > 0:
target = close - trail_pts * step
if self._stop_price == 0 or target - self._stop_price >= trail_step * step:
self._stop_price = target
# Exit check
if (self._stop_price > 0 and low <= self._stop_price) or (self._tp_price > 0 and high >= self._tp_price):
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._tp_price = 0.0
elif self.Position < 0:
if be_pts > 0 and self._entry_price - close >= be_pts * step and (self._stop_price == 0 or self._stop_price > self._entry_price):
self._stop_price = self._entry_price
if trail_pts > 0:
target = close + trail_pts * step
if self._stop_price == 0 or self._stop_price - target >= trail_step * step:
self._stop_price = target
if (self._stop_price > 0 and high >= self._stop_price) or (self._tp_price > 0 and low <= self._tp_price):
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
self._tp_price = 0.0
# Entry signals
if self._prev_signal >= 20.0 and signal < 20.0 and self._prev_wpr >= -80.0 and wpr < -80.0 and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - sl_pts * step
self._tp_price = close + tp_pts * step
elif self._prev_signal <= 80.0 and signal > 80.0 and self._prev_wpr <= -20.0 and wpr > -20.0 and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._stop_price = close + sl_pts * step
self._tp_price = close - tp_pts * step
self._prev_signal = signal
self._prev_wpr = wpr
def CreateClone(self):
return the_master_mind_2_strategy()