Exp Martin V2-Strategie
Die Exp Martin V2-Strategie implementiert einen exponentiellen Martingale-Ansatz. Es wird immer nur eine einzige offene Position gehalten, und nach jedem Trade wird die nächste Richtung und das Volumen basierend auf dem Gewinn des letzten Geschäfts entschieden.
Die Strategie beginnt mit einem vordefinierten Auftragstyp (Kauf oder Verkauf) und einem Startvolumen. Jede Position erhält einen festen Take-Profit und Stop-Loss. Wenn ein Trade mit Gewinn endet, wird eine neue Position gleicher Art mit dem Startvolumen eröffnet. Endet der Trade mit Verlust, wird die Richtung umgekehrt und das Volumen mit einem angegebenen Faktor multipliziert. Die Multiplikation setzt sich nach jedem Verlust fort, bis eine maximale Anzahl von Multiplikationen erreicht ist; dann wird das Volumen auf den Startwert zurückgesetzt.
Dies erzeugt eine eskalierende Sequenz entgegengesetzter Trades, die darauf abzielt, frühere Verluste zu erholen, sobald eine profitable Bewegung eintritt.
Details
- Einstiegslogik:
- Anfangsposition gemäß Start Type (0 - Kauf, 1 - Verkauf) mit dem Start Volume eröffnen.
- Nach einem profitablen Trade dieselbe Richtung mit dem Startvolumen wiederholen.
- Nach einem verlustbringenden Trade die Richtung umkehren und das Volumen mit Factor multiplizieren, bis Limit Multiplikationen erreicht sind.
- Long/Short: Beide, abhängig von der aktuellen Sequenz.
- Ausstiegslogik:
- Positionen werden geschlossen, wenn der Preis die konfigurierten Take Profit- oder Stop Loss-Niveaus erreicht.
- Stops: Feste Stop-Loss und Take-Profit in Punkten.
- Filter: Keine.
- Positionsverwaltung: Nur eine Position ist gleichzeitig offen.
Verwenden Sie diese Strategie, um mit Martingale-Geldmanagement in StockSharp ohne zusätzliche Indikatoren zu experimentieren.
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>
/// Exponential martingale strategy.
/// </summary>
public class ExpMartinV2Strategy : Strategy
{
private readonly StrategyParam<decimal> _startVolume;
private readonly StrategyParam<decimal> _factor;
private readonly StrategyParam<int> _limit;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _startType;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cycleCooldownBars;
private decimal _currentVolume;
private decimal _maxVolume;
private bool _needOpenPosition = true;
private int _direction;
private decimal _entryPrice;
private decimal _longTake;
private decimal _longStop;
private decimal _shortTake;
private decimal _shortStop;
private int _cooldownRemaining;
/// <summary>
/// Initial order volume.
/// </summary>
public decimal StartVolume { get => _startVolume.Value; set => _startVolume.Value = value; }
/// <summary>
/// Volume multiplier after a loss.
/// </summary>
public decimal Factor { get => _factor.Value; set => _factor.Value = value; }
/// <summary>
/// Maximum number of volume multiplications.
/// </summary>
public int Limit { get => _limit.Value; set => _limit.Value = value; }
/// <summary>
/// Stop loss in price steps.
/// </summary>
public int StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
/// <summary>
/// Take profit in price steps.
/// </summary>
public int TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Starting order type: 0 - buy, 1 - sell.
/// </summary>
public int StartType { get => _startType.Value; set => _startType.Value = value; }
/// <summary>
/// Candle type used for processing.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Closed candles to wait before starting the next martingale cycle.
/// </summary>
public int CycleCooldownBars { get => _cycleCooldownBars.Value; set => _cycleCooldownBars.Value = value; }
public ExpMartinV2Strategy()
{
_startVolume = Param(nameof(StartVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Start Volume", "Initial order volume", "General");
_factor = Param(nameof(Factor), 2m)
.SetGreaterThanZero()
.SetDisplay("Factor", "Volume multiplier", "General");
_limit = Param(nameof(Limit), 5)
.SetGreaterThanZero()
.SetDisplay("Limit", "Max multiplication count", "General");
_stopLoss = Param(nameof(StopLoss), 100)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in points", "Risk");
_takeProfit = Param(nameof(TakeProfit), 100)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Profit target in points", "Risk");
_startType = Param(nameof(StartType), 0)
.SetDisplay("Start Type", "0-Buy, 1-Sell", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_cycleCooldownBars = Param(nameof(CycleCooldownBars), 2)
.SetNotNegative()
.SetDisplay("Cycle Cooldown Bars", "Closed candles to wait before the next entry cycle", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentVolume = 0m;
_maxVolume = 0m;
_needOpenPosition = true;
_direction = 0;
_entryPrice = 0m;
_longTake = 0m;
_longStop = 0m;
_shortTake = 0m;
_shortStop = 0m;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_currentVolume = StartVolume;
_direction = StartType == 0 ? 1 : -1;
_maxVolume = StartVolume;
for (var i = 0; i < Limit; i++)
_maxVolume = RoundVolume(_maxVolume * Factor);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var step = Security.PriceStep ?? 1m;
if (Position > 0)
{
if (candle.HighPrice >= _longTake)
{
SellMarket(Position);
PrepareNext(true);
}
else if (candle.LowPrice <= _longStop)
{
SellMarket(Position);
PrepareNext(false);
}
}
else if (Position < 0)
{
if (candle.LowPrice <= _shortTake)
{
BuyMarket(-Position);
PrepareNext(true);
}
else if (candle.HighPrice >= _shortStop)
{
BuyMarket(-Position);
PrepareNext(false);
}
}
if (_needOpenPosition && Position == 0 && _cooldownRemaining == 0)
{
_entryPrice = candle.ClosePrice;
if (_direction == 1)
{
BuyMarket(_currentVolume);
_longTake = _entryPrice + TakeProfit * step;
_longStop = _entryPrice - StopLoss * step;
}
else
{
SellMarket(_currentVolume);
_shortTake = _entryPrice - TakeProfit * step;
_shortStop = _entryPrice + StopLoss * step;
}
_needOpenPosition = false;
}
}
private void PrepareNext(bool wasProfit)
{
if (wasProfit)
{
_currentVolume = StartVolume;
}
else
{
_direction = -_direction;
_currentVolume = RoundVolume(_currentVolume * Factor);
if (_currentVolume > _maxVolume)
_currentVolume = StartVolume;
}
_needOpenPosition = true;
_cooldownRemaining = CycleCooldownBars;
}
private decimal RoundVolume(decimal volume)
{
var step = Security.VolumeStep ?? 1m;
return Math.Ceiling(volume / step) * step;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
import math
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class exp_martin_v2_strategy(Strategy):
"""
Exponential martingale strategy.
Opens positions and doubles volume on loss, resets on profit.
"""
def __init__(self):
super(exp_martin_v2_strategy, self).__init__()
self._start_volume = self.Param("StartVolume", 1.0) \
.SetDisplay("Start Volume", "Initial order volume", "General")
self._factor = self.Param("Factor", 2.0) \
.SetDisplay("Factor", "Volume multiplier", "General")
self._limit = self.Param("Limit", 5) \
.SetDisplay("Limit", "Max multiplication count", "General")
self._stop_loss = self.Param("StopLoss", 100) \
.SetDisplay("Stop Loss", "Loss limit in points", "Risk")
self._take_profit = self.Param("TakeProfit", 100) \
.SetDisplay("Take Profit", "Profit target in points", "Risk")
self._start_type = self.Param("StartType", 0) \
.SetDisplay("Start Type", "0-Buy, 1-Sell", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown_bars = self.Param("CycleCooldownBars", 2) \
.SetDisplay("Cycle Cooldown Bars", "Bars to wait before next cycle", "General")
self._current_volume = 0.0
self._max_volume = 0.0
self._need_open = True
self._direction = 0
self._entry_price = 0.0
self._long_take = 0.0
self._long_stop = 0.0
self._short_take = 0.0
self._short_stop = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_martin_v2_strategy, self).OnReseted()
self._current_volume = 0.0
self._max_volume = 0.0
self._need_open = True
self._direction = 0
self._entry_price = 0.0
self._long_take = 0.0
self._long_stop = 0.0
self._short_take = 0.0
self._short_stop = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(exp_martin_v2_strategy, self).OnStarted2(time)
sv = float(self._start_volume.Value)
self._current_volume = sv
self._direction = 1 if self._start_type.Value == 0 else -1
self._max_volume = sv
f = float(self._factor.Value)
for i in range(self._limit.Value):
self._max_volume = self._round_volume(self._max_volume * f)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self.Position > 0:
if high >= self._long_take:
self.SellMarket(self.Position)
self._prepare_next(True)
elif low <= self._long_stop:
self.SellMarket(self.Position)
self._prepare_next(False)
elif self.Position < 0:
if low <= self._short_take:
self.BuyMarket(-self.Position)
self._prepare_next(True)
elif high >= self._short_stop:
self.BuyMarket(-self.Position)
self._prepare_next(False)
if self._need_open and self.Position == 0 and self._cooldown_remaining == 0:
self._entry_price = close
tp = self._take_profit.Value
sl = self._stop_loss.Value
if self._direction == 1:
self.BuyMarket(self._current_volume)
self._long_take = self._entry_price + tp * step
self._long_stop = self._entry_price - sl * step
else:
self.SellMarket(self._current_volume)
self._short_take = self._entry_price - tp * step
self._short_stop = self._entry_price + sl * step
self._need_open = False
def _prepare_next(self, was_profit):
if was_profit:
self._current_volume = float(self._start_volume.Value)
else:
self._direction = -self._direction
self._current_volume = self._round_volume(self._current_volume * float(self._factor.Value))
if self._current_volume > self._max_volume:
self._current_volume = float(self._start_volume.Value)
self._need_open = True
self._cooldown_remaining = self._cooldown_bars.Value
def _round_volume(self, volume):
step = 1.0
if self.Security is not None and self.Security.VolumeStep is not None:
step = float(self.Security.VolumeStep)
if step <= 0:
step = 1.0
return math.ceil(volume / step) * step
def CreateClone(self):
return exp_martin_v2_strategy()