Super Take-Strategie
Diese Strategie wechselt zwischen Long- und Short-Positionen und erhöht den Take Profit nach jedem Verlusthandel mithilfe eines Martingale-Multiplikators. Der Stop Loss ist fest, während der Take Profit nach einem Gewinnhandel auf den Basiswert zurückgesetzt wird. Durch ständigen Richtungswechsel und Anpassung der Ziele nach Verlusten versucht die Strategie, frühere Drawdowns auszugleichen.
Eine neue Position wird nur eröffnet, wenn keine Position aktiv ist. Der erste Trade ist standardmäßig Long. Jeder nachfolgende Trade wird in der entgegengesetzten Richtung der zuletzt geschlossenen Position eröffnet.
Details
- Einstiegskriterien:
- Long: Keine aktive Position und die zuletzt geschlossene Position war Short oder nicht vorhanden.
- Short: Keine aktive Position und die zuletzt geschlossene Position war Long.
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Position schließen, wenn der Preis den dynamischen Take Profit oder den festen Stop Loss erreicht.
- Stops: Fester Stop Loss, dynamischer Take Profit mit Martingale nach Verlusthandeln.
- Standardwerte:
TakeProfit= 10StopLoss= 15MartinFactor= 1.8
- Filter:
- Kategorie: Umkehr
- Richtung: Beide
- Indikatoren: Keine
- Stops: Ja
- Komplexität: Einfach
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Hoch
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Alternates between long and short entries after actual fills, with a
/// fill-anchored stop and a target increased after a realized losing trade.
/// Entry scheduling uses finished candles; protective exits use fresh bid/ask quotes.
/// </summary>
public class SuperTakeStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _martinFactor;
private readonly StrategyParam<DataType> _candleType;
private Order _entryOrder;
private Order _exitOrder;
private Order _pendingOrder;
private bool _submittingEntry;
private bool _submittingExit;
private bool _entryLong;
private decimal _entryVolume;
private decimal _entryValue;
private decimal _remainingVolume;
private decimal _realizedPnl;
private decimal _currentTakeProfit;
private decimal _lastTakeProfitDistance;
private bool? _lastClosedWasBuy;
private bool _lastTradeWasLoss;
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal MartinFactor { get => _martinFactor.Value; set => _martinFactor.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public SuperTakeStrategy()
{
_takeProfit = Param(nameof(TakeProfit), 10m).SetGreaterThanZero()
.SetDisplay("Take Profit", "Base price-distance target", "Risk");
_stopLoss = Param(nameof(StopLoss), 15m).SetGreaterThanZero()
.SetDisplay("Stop Loss", "Price-distance stop", "Risk");
_martinFactor = Param(nameof(MartinFactor), 1.8m).SetGreaterThanZero()
.SetDisplay("Martingale Factor", "Take-profit distance multiplier after loss", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
// The registration event can precede a synchronous fill, in which case
// storing only BuyMarket/SellMarket's return value would miss the trade.
OrderRegistering += order =>
{
if (_submittingEntry) _entryOrder = order;
else if (_submittingExit) _exitOrder = order;
_pendingOrder = order;
};
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
private bool HasPendingOrder => _pendingOrder is not null &&
_pendingOrder.State is not (OrderStates.Done or OrderStates.Failed);
protected override void OnReseted()
{
base.OnReseted();
ResetLocalState();
}
private void ResetLocalState()
{
_entryOrder = _exitOrder = _pendingOrder = null;
_submittingEntry = _submittingExit = false;
_entryLong = true;
_entryVolume = _entryValue = _remainingVolume = _realizedPnl = 0m;
_currentTakeProfit = _lastTakeProfitDistance = 0m;
_lastClosedWasBuy = null;
_lastTradeWasLoss = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetLocalState();
_lastTakeProfitDistance = TakeProfit;
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ProcessQuote).Start();
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished || Position != 0m ||
_remainingVolume != 0m || HasPendingOrder || !IsFormedAndOnlineAndAllowTrading())
return;
_entryLong = _lastClosedWasBuy is not true;
// Decimal arithmetic is finite; clamp an unreachable martingale target
// rather than crashing after repeated realized losses.
if (_lastTradeWasLoss)
{
var factor = MartinFactor;
_currentTakeProfit = factor > 1m &&
_lastTakeProfitDistance > decimal.MaxValue / factor
? decimal.MaxValue
: _lastTakeProfitDistance * factor;
}
else
_currentTakeProfit = TakeProfit;
_entryVolume = _entryValue = _realizedPnl = 0m;
_exitOrder = null;
_submittingEntry = true;
try
{
_entryOrder = _entryLong ? BuyMarket(Volume) : SellMarket(Volume);
_pendingOrder = _entryOrder;
}
finally
{
_submittingEntry = false;
}
}
/// <summary>Recognize executions only; a submitted order never creates a hypothetical fill.</summary>
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade?.Order is null || trade.Trade is null)
return;
var volume = trade.Trade.Volume;
var price = trade.Trade.Price;
if (trade.Order == _entryOrder)
{
_entryVolume += volume;
_entryValue += volume * price;
_remainingVolume += volume;
}
else if (trade.Order == _exitOrder && _entryVolume > 0m)
{
var entry = _entryValue / _entryVolume;
_realizedPnl += (_entryLong ? price - entry : entry - price) * volume;
_remainingVolume = Math.Max(0m, _remainingVolume - volume);
if (_remainingVolume == 0m)
{
_lastClosedWasBuy = _entryLong;
_lastTradeWasLoss = _realizedPnl < 0m;
_lastTakeProfitDistance = _currentTakeProfit;
}
}
}
private void ProcessQuote(Level1ChangeMessage quote)
{
if (Position == 0m || _remainingVolume <= 0m || _entryVolume <= 0m ||
HasPendingOrder || !IsFormedAndOnlineAndAllowTrading())
return;
var field = Position > 0m ? Level1Fields.BestBidPrice : Level1Fields.BestAskPrice;
if (!quote.Changes.TryGetValue(field, out var raw) || raw is not decimal price || price <= 0m)
return;
var entry = _entryValue / _entryVolume;
var profit = Position > 0m ? price - entry : entry - price;
if (profit < _currentTakeProfit && profit > -StopLoss)
return;
_submittingExit = true;
try
{
_exitOrder = Position > 0m ? SellMarket(Math.Abs(Position)) : BuyMarket(Math.Abs(Position));
_pendingOrder = _exitOrder;
}
finally
{
_submittingExit = false;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Convert, Decimal
from StockSharp.Messages import DataType, CandleStates, OrderStates, Level1Fields, Sides
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class super_take_strategy(Strategy):
"""Alternating trades with fill-based Labouchere-free take-profit martingale.
Candle closes schedule entries ONLY. Protection triggers on fresh executable
bid/ask quotes after a real fill; pending or partially filled orders do not
create imaginary closes or permit duplicate entries/exits.
"""
def __init__(self):
super(super_take_strategy, self).__init__()
self._take_profit = self.Param("TakeProfit", 10.0).SetDisplay(
"Take Profit", "Base price-distance target", "Risk")
self._stop_loss = self.Param("StopLoss", 15.0).SetDisplay(
"Stop Loss", "Price-distance stop", "Risk")
self._martin_factor = self.Param("MartinFactor", 1.8).SetDisplay(
"Martingale Factor", "Take-profit distance multiplier after loss", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay(
"Candle Type", "Type of candles", "General")
self._reset_state()
self.OrderRegistering += self._track_order
def _track_order(self, order):
if self._submitting_entry:
self._entry_order = order
elif self._submitting_exit:
self._exit_order = order
self._pending_order = order
@property
def TakeProfit(self): return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value): self._take_profit.Value = value
@property
def StopLoss(self): return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value): self._stop_loss.Value = value
@property
def MartinFactor(self): return self._martin_factor.Value
@MartinFactor.setter
def MartinFactor(self, value): self._martin_factor.Value = value
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def _reset_state(self):
zero = Convert.ToDecimal(0)
self._submitting_entry = False
self._submitting_exit = False
self._entry_order = None
self._exit_order = None
self._pending_order = None
self._entry_long = True
self._entry_volume = zero
self._entry_value = zero
self._remaining_volume = zero
self._realized_pnl = zero
self._current_take_profit = zero
self._last_take_profit_distance = zero
self._last_closed_was_buy = None
self._last_trade_was_loss = False
def OnReseted(self):
super(super_take_strategy, self).OnReseted()
self._reset_state()
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType), (self.Security, DataType.Level1)]
def OnStarted2(self, time):
super(super_take_strategy, self).OnStarted2(time)
self._reset_state()
self._last_take_profit_distance = Convert.ToDecimal(self.TakeProfit)
self.SubscribeCandles(self.CandleType).Bind(self.ProcessCandle).Start()
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
sub = Subscription(DataType.Level1, self.Security)
sub.MarketData.BuildField = field
self.SubscribeLevel1(sub).Bind(self._process_quote).Start()
def _has_pending(self):
return self._pending_order is not None and self._pending_order.State not in (
OrderStates.Done, OrderStates.Failed)
def ProcessCandle(self, candle):
if (candle.State != CandleStates.Finished or self.Position != 0 or
self._remaining_volume != 0 or self._has_pending() or
not self.IsFormedAndOnlineAndAllowTrading()):
return
self._entry_long = self._last_closed_was_buy is not True
base = Convert.ToDecimal(self.TakeProfit)
factor = Convert.ToDecimal(self.MartinFactor)
if self._last_trade_was_loss:
limit = Decimal.MaxValue / factor if factor > 1 else Decimal.MaxValue
self._current_take_profit = (Decimal.MaxValue if self._last_take_profit_distance > limit
else self._last_take_profit_distance * factor)
else:
self._current_take_profit = base
self._entry_volume = Convert.ToDecimal(0)
self._entry_value = Convert.ToDecimal(0)
self._realized_pnl = Convert.ToDecimal(0)
self._exit_order = None
self._submitting_entry = True
try:
self._entry_order = self.BuyMarket(self.Volume) if self._entry_long else self.SellMarket(self.Volume)
self._pending_order = self._entry_order
finally:
self._submitting_entry = False
def OnOwnTradeReceived(self, trade):
super(super_take_strategy, self).OnOwnTradeReceived(trade)
if trade.Order is None or trade.Trade is None:
return
volume = Convert.ToDecimal(trade.Trade.TradeVolume)
price = Convert.ToDecimal(trade.Trade.TradePrice)
if trade.Order == self._entry_order:
self._entry_volume += volume
self._entry_value += volume * price
self._remaining_volume += volume
elif trade.Order == self._exit_order and self._entry_volume > 0:
entry = self._entry_value / self._entry_volume
self._realized_pnl += (price - entry if self._entry_long else entry - price) * volume
self._remaining_volume = max(Convert.ToDecimal(0), self._remaining_volume - volume)
if self._remaining_volume == 0:
self._last_closed_was_buy = self._entry_long
self._last_trade_was_loss = self._realized_pnl < 0
self._last_take_profit_distance = self._current_take_profit
def _process_quote(self, quote):
if (self.Position == 0 or self._remaining_volume <= 0 or self._entry_volume <= 0 or
self._has_pending() or not self.IsFormedAndOnlineAndAllowTrading()):
return
field = Level1Fields.BestBidPrice if self.Position > 0 else Level1Fields.BestAskPrice
if not quote.Changes.ContainsKey(field):
return
price = Convert.ToDecimal(quote.Changes[field])
if price <= 0:
return
entry = self._entry_value / self._entry_volume
profit = price - entry if self.Position > 0 else entry - price
if profit >= self._current_take_profit or profit <= -Convert.ToDecimal(self.StopLoss):
volume = abs(self.Position)
self._submitting_exit = True
try:
self._exit_order = self.SellMarket(volume) if self.Position > 0 else self.BuyMarket(volume)
self._pending_order = self._exit_order
finally:
self._submitting_exit = False
def CreateClone(self):
return super_take_strategy()