Stochastic Martingale-Strategie
Diese Strategie kombiniert einen klassischen Stochastic-Oszillator-Einstieg mit einer Martingale-artigen Mittelung. Eine Position wird geöffnet, wenn die %K-Linie die %D-Linie kreuzt und der Oszillator über/unter konfigurierbaren Zonen liegt. Wenn sich der Kurs um einen definierten Schritt gegen die Position bewegt, erhöht die Strategie das Volumen um einen Multiplikator. Positionen werden geschlossen, wenn der angesammelte Gewinn eine definierte Anzahl von Punkten erreicht.
Details
- Einstiegskriterien
- Long: %K > %D und %D > ZoneBuy
- Short: %K < %D und %D < ZoneSell
- Mittelung
- Zusätzliche Orders werden alle
StepPunkte platziert (oderStep * Orderanzahlin Modus 1). - Das Volumen jeder neuen Order wird mit
Multmultipliziert.
- Zusätzliche Orders werden alle
- Ausstiegskriterien
- Long: Kurs ≥ letzter Kaufkurs +
ProfitFactor * OrderanzahlPunkte. - Short: Kurs ≤ letzter Verkaufskurs −
ProfitFactor * OrderanzahlPunkte.
- Long: Kurs ≥ letzter Kaufkurs +
- Parameter umfassen Schrittgröße, Schrittmodus, Gewinnfaktor, Multiplikator, Ausgangsvolumina und Stochastic-Perioden.
- Filter
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Stochastic
- Stops: Nein
- Komplexität: Mittel
- Zeitrahmen: Kurzfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Hoch
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 Martingale strategy.
/// Uses Stochastic oscillator for signals and martingale averaging.
/// </summary>
public class StochasticMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _step;
private readonly StrategyParam<int> _stepMode;
private readonly StrategyParam<int> _profitFactor;
private readonly StrategyParam<decimal> _mult;
private readonly StrategyParam<decimal> _buyVolume;
private readonly StrategyParam<decimal> _sellVolume;
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<decimal> _zoneBuy;
private readonly StrategyParam<decimal> _zoneSell;
private readonly StrategyParam<bool> _reverse;
private readonly StrategyParam<DataType> _candleType;
private decimal _lastBuyPrice;
private decimal _lastBuyVolume;
private decimal _lastSellPrice;
private decimal _lastSellVolume;
private int _buyCount;
private int _sellCount;
/// <summary>Price step in points for averaging.</summary>
public int Step { get => _step.Value; set => _step.Value = value; }
/// <summary>Step mode: 0 - fixed, 1 - multiplied by orders count.</summary>
public int StepMode { get => _stepMode.Value; set => _stepMode.Value = value; }
/// <summary>Points for take profit per order.</summary>
public int ProfitFactor { get => _profitFactor.Value; set => _profitFactor.Value = value; }
/// <summary>Volume multiplier for averaging.</summary>
public decimal Mult { get => _mult.Value; set => _mult.Value = value; }
/// <summary>Initial buy volume.</summary>
public decimal BuyVolume { get => _buyVolume.Value; set => _buyVolume.Value = value; }
/// <summary>Initial sell volume.</summary>
public decimal SellVolume { get => _sellVolume.Value; set => _sellVolume.Value = value; }
/// <summary>Stochastic %K period.</summary>
public int KPeriod { get => _kPeriod.Value; set => _kPeriod.Value = value; }
/// <summary>Stochastic %D period.</summary>
public int DPeriod { get => _dPeriod.Value; set => _dPeriod.Value = value; }
/// <summary>Oversold level.</summary>
public decimal ZoneBuy { get => _zoneBuy.Value; set => _zoneBuy.Value = value; }
/// <summary>Overbought level.</summary>
public decimal ZoneSell { get => _zoneSell.Value; set => _zoneSell.Value = value; }
/// <summary>Reverse entry direction.</summary>
public bool Reverse { get => _reverse.Value; set => _reverse.Value = value; }
/// <summary>Candle type.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Initialize <see cref="StochasticMartingaleStrategy"/>.</summary>
public StochasticMartingaleStrategy()
{
_step = Param(nameof(Step), 25)
.SetGreaterThanZero()
.SetDisplay("Step", "Price step in points for averaging", "Martingale");
_stepMode = Param(nameof(StepMode), 0)
.SetDisplay("Step Mode", "0 - fixed step, 1 - step multiplied by orders count", "Martingale");
_profitFactor = Param(nameof(ProfitFactor), 20)
.SetGreaterThanZero()
.SetDisplay("Profit Factor", "Points for take profit per order", "Martingale");
_mult = Param(nameof(Mult), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Volume multiplier for averaging", "Martingale");
_buyVolume = Param(nameof(BuyVolume), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Buy Volume", "Initial buy volume", "General");
_sellVolume = Param(nameof(SellVolume), 0.01m)
.SetGreaterThanZero()
.SetDisplay("Sell Volume", "Initial sell volume", "General");
_kPeriod = Param(nameof(KPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("%K Period", "Stochastic %K period", "Indicators");
_dPeriod = Param(nameof(DPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("%D Period", "Stochastic %D period", "Indicators");
_zoneBuy = Param(nameof(ZoneBuy), 65m)
.SetDisplay("Zone Buy", "Oversold level", "Indicators");
_zoneSell = Param(nameof(ZoneSell), 70m)
.SetDisplay("Zone Sell", "Overbought level", "Indicators");
_reverse = Param(nameof(Reverse), false)
.SetDisplay("Reverse", "Reverses entry direction", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastBuyPrice = 0;
_lastBuyVolume = 0;
_lastSellPrice = 0;
_lastSellVolume = 0;
_buyCount = 0;
_sellCount = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var stochastic = new StochasticOscillator
{
K = { Length = KPeriod },
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 = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal kValue || stoch.D is not decimal dValue)
return;
var step = Step * (Security.PriceStep ?? 0m);
var profit = ProfitFactor * (Security.PriceStep ?? 0m);
var price = candle.ClosePrice;
if (_buyCount > 0 && Position > 0)
{
if ((StepMode == 0 && price <= _lastBuyPrice - step) ||
(StepMode == 1 && price <= _lastBuyPrice - step * _buyCount))
{
var volume = CheckVolume(_lastBuyVolume * Mult);
if (volume > 0)
{
BuyMarket(volume);
_lastBuyPrice = price;
_lastBuyVolume = volume;
_buyCount++;
}
}
if (price >= _lastBuyPrice + profit * _buyCount)
{
SellMarket(Math.Abs(Position));
_buyCount = 0;
}
}
else if (_sellCount > 0 && Position < 0)
{
if ((StepMode == 0 && price >= _lastSellPrice + step) ||
(StepMode == 1 && price >= _lastSellPrice + step * _sellCount))
{
var volume = CheckVolume(_lastSellVolume * Mult);
if (volume > 0)
{
SellMarket(volume);
_lastSellPrice = price;
_lastSellVolume = volume;
_sellCount++;
}
}
if (price <= _lastSellPrice - profit * _sellCount)
{
BuyMarket(Math.Abs(Position));
_sellCount = 0;
}
}
else if (Position == 0)
{
if (kValue > dValue && dValue > ZoneBuy)
{
if (!Reverse)
{
var volume = CheckVolume(BuyVolume);
if (volume > 0)
{
BuyMarket(volume);
_lastBuyPrice = price;
_lastBuyVolume = volume;
_buyCount = 1;
}
}
else
{
var volume = CheckVolume(SellVolume);
if (volume > 0)
{
SellMarket(volume);
_lastSellPrice = price;
_lastSellVolume = volume;
_sellCount = 1;
}
}
}
else if (kValue < dValue && dValue < ZoneSell)
{
if (!Reverse)
{
var volume = CheckVolume(SellVolume);
if (volume > 0)
{
SellMarket(volume);
_lastSellPrice = price;
_lastSellVolume = volume;
_sellCount = 1;
}
}
else
{
var volume = CheckVolume(BuyVolume);
if (volume > 0)
{
BuyMarket(volume);
_lastBuyPrice = price;
_lastBuyVolume = volume;
_buyCount = 1;
}
}
}
}
}
private decimal CheckVolume(decimal volume)
{
var step = Security.VolumeStep ?? 0m;
if (step > 0)
volume = step * Math.Floor(volume / step);
if (volume <= 0)
volume = 0m;
return volume;
}
}
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.Indicators import StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class stochastic_martingale_strategy(Strategy):
def __init__(self):
super(stochastic_martingale_strategy, self).__init__()
self._step = self.Param("Step", 25)
self._step_mode = self.Param("StepMode", 0)
self._profit_factor = self.Param("ProfitFactor", 20)
self._mult = self.Param("Mult", 1.5)
self._buy_volume_param = self.Param("BuyVolume", 0.01)
self._sell_volume_param = self.Param("SellVolume", 0.01)
self._k_period = self.Param("KPeriod", 200)
self._d_period = self.Param("DPeriod", 20)
self._zone_buy = self.Param("ZoneBuy", 65.0)
self._zone_sell = self.Param("ZoneSell", 70.0)
self._reverse = self.Param("Reverse", False)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._last_buy_price = 0.0
self._last_buy_volume = 0.0
self._last_sell_price = 0.0
self._last_sell_volume = 0.0
self._buy_count = 0
self._sell_count = 0
@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(stochastic_martingale_strategy, self).OnStarted2(time)
self._last_buy_price = 0.0
self._last_buy_volume = 0.0
self._last_sell_price = 0.0
self._last_sell_volume = 0.0
self._buy_count = 0
self._sell_count = 0
stochastic = StochasticOscillator()
stochastic.K.Length = int(self._k_period.Value)
stochastic.D.Length = int(self._d_period.Value)
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stochastic, self.ProcessCandle).Start()
def _check_volume(self, volume):
sec = self.Security
vol_step = float(sec.VolumeStep) if sec is not None and sec.VolumeStep is not None else 0.0
if vol_step > 0:
volume = vol_step * Math.Floor(volume / vol_step)
if volume <= 0:
volume = 0.0
return volume
def ProcessCandle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
k_val = stoch_value.K
d_val = stoch_value.D
if k_val is None or d_val is None:
return
k_val = float(k_val)
d_val = float(d_val)
price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0
step = float(self._step.Value) * price_step
profit = float(self._profit_factor.Value) * price_step
price = float(candle.ClosePrice)
mult = float(self._mult.Value)
zone_buy = float(self._zone_buy.Value)
zone_sell = float(self._zone_sell.Value)
reverse = bool(self._reverse.Value)
buy_vol = float(self._buy_volume_param.Value)
sell_vol = float(self._sell_volume_param.Value)
pos = float(self.Position)
step_mode = int(self._step_mode.Value)
if self._buy_count > 0 and pos > 0:
if (step_mode == 0 and price <= self._last_buy_price - step) or \
(step_mode == 1 and price <= self._last_buy_price - step * self._buy_count):
volume = self._check_volume(self._last_buy_volume * mult)
if volume > 0:
self.BuyMarket(volume)
self._last_buy_price = price
self._last_buy_volume = volume
self._buy_count += 1
if price >= self._last_buy_price + profit * self._buy_count:
self.SellMarket(abs(float(self.Position)))
self._buy_count = 0
elif self._sell_count > 0 and pos < 0:
if (step_mode == 0 and price >= self._last_sell_price + step) or \
(step_mode == 1 and price >= self._last_sell_price + step * self._sell_count):
volume = self._check_volume(self._last_sell_volume * mult)
if volume > 0:
self.SellMarket(volume)
self._last_sell_price = price
self._last_sell_volume = volume
self._sell_count += 1
if price <= self._last_sell_price - profit * self._sell_count:
self.BuyMarket(abs(float(self.Position)))
self._sell_count = 0
elif float(self.Position) == 0:
if k_val > d_val and d_val > zone_buy:
if not reverse:
volume = self._check_volume(buy_vol)
if volume > 0:
self.BuyMarket(volume)
self._last_buy_price = price
self._last_buy_volume = volume
self._buy_count = 1
else:
volume = self._check_volume(sell_vol)
if volume > 0:
self.SellMarket(volume)
self._last_sell_price = price
self._last_sell_volume = volume
self._sell_count = 1
elif k_val < d_val and d_val < zone_sell:
if not reverse:
volume = self._check_volume(sell_vol)
if volume > 0:
self.SellMarket(volume)
self._last_sell_price = price
self._last_sell_volume = volume
self._sell_count = 1
else:
volume = self._check_volume(buy_vol)
if volume > 0:
self.BuyMarket(volume)
self._last_buy_price = price
self._last_buy_volume = volume
self._buy_count = 1
def OnReseted(self):
super(stochastic_martingale_strategy, self).OnReseted()
self._last_buy_price = 0.0
self._last_buy_volume = 0.0
self._last_sell_price = 0.0
self._last_sell_volume = 0.0
self._buy_count = 0
self._sell_count = 0
def CreateClone(self):
return stochastic_martingale_strategy()