Die Puncher-Strategie
Überblick
Die Puncher-Strategie ist ein Impulsumkehrsystem, das aus dem ursprünglichen MetaTrader 4-Expertenberater „The Puncher von L. Bigger“ abgeleitet wurde. Es kombiniert einen langsamen Stochastic-Oszillator mit einem klassischen RSI-Filter, um extrem überkaufte und überverkaufte Bedingungen zu handeln. Wenn beide Oszillatoren übereinstimmen, dass der Markt erweitert wird, sucht die Strategie nach einer Umkehr am Ende der Kerze und gibt eine Marktorder in die entgegengesetzte Richtung ein.
Handelslogik
- Kauf-Setup: Wird ausgelöst, wenn die Signallinie Stochastic und RSI gleichzeitig unter den überverkauften Wert fallen. Die bestehende Short-Position (falls vorhanden) wird zuerst geschlossen und dann eine neue Long-Position eröffnet.
- Verkaufs-Setup: Wird ausgelöst, wenn beide Oszillatoren über das überkaufte Niveau steigen. Alle offenen Long-Positionen werden liquidiert, bevor eine neue Short-Position platziert wird.
- Ausstiegsregeln: Positionen werden durch entgegengesetzte Signale oder durch Schutzregeln (Stop-Loss, Take-Profit, Break-Even und Trailing Stop) geschlossen.
Die Strategie verarbeitet nur abgeschlossene Kerzen aus dem ausgewählten Zeitrahmen, um Intra-Bar-Rauschen zu vermeiden, und reproduziert das „Trade at Bar Close“-Verhalten der Quelle EA.
Risikomanagement
- Stop-Loss / Take-Profit: Optionale feste Distanzen, gemessen in Pips. Wenn deaktiviert (Null), wird der entsprechende Schutz ignoriert.
- Break-Even: Verschiebt den Stop auf den Einstiegspreis, nachdem der Trade den angeforderten Gewinnpuffer angesammelt hat.
- Trailing Stop: Folgt dem Preis mit einem konfigurierbaren Abstand und einem minimalen Schritt, sodass der Stop erst dann angezogen wird, wenn der Preis ausreichend gestiegen ist.
- Volumen: Aufträge verwenden einen festen Volumenparameter, der die Losgrößeneingabe der MT4-Version widerspiegelt.
Parameter
| Name | Beschreibung | Standard |
|---|---|---|
OrderVolume |
Handelsvolumen für Neuzugänge. | 1 |
StochasticLength |
Lookback-Länge des Stochastic-Oszillators (%K). | 100 |
StochasticSignalPeriod |
Glättungszeitraum von %K vor dem Anlegen der Signalleitung. | 3 |
StochasticSmoothingPeriod |
Glättungszeitraum für die %D-Signalleitung. | 3 |
RsiPeriod |
Berechnungszeitraum des Filters RSI. | 14 |
OversoldLevel |
Von den Oszillatoren gemeinsamer Schwellenwert zur Erkennung überverkaufter Bedingungen. | 30 |
OverboughtLevel |
Von den Oszillatoren gemeinsamer Schwellenwert zur Erkennung überkaufter Bedingungen. | 70 |
StopLossPips |
Abstand des Schutzstopps (0 deaktiviert ihn). | 2000 |
TakeProfitPips |
Abstand des Gewinnziels (0 deaktiviert es). | 0 |
TrailingStopPips |
Trailing-Stop-Distanz (0 deaktiviert ihn). | 0 |
TrailingStepPips |
Minimale günstige Bewegung vor dem Anziehen des Trailing Stops. | 1 |
BreakEvenPips |
Erforderlicher Gewinn, bevor der Stop auf die Gewinnschwelle verschoben wird. | 0 |
CandleType |
Datentyp, der zum Erstellen von Kerzen verwendet wird. | M15 |
Notizen
- Die Pip-Größe wird aus dem Preisschritt oder den Dezimalstellen des Wertpapiers abgeleitet, um sicherzustellen, dass die Stop- und Trailing-Distanzen die Präzision des Instruments respektieren.
- Die Strategie eignet sich für diskretionäre Backtests, bei denen das ursprüngliche EA verwendet wurde, und kann als Grundlage für weitere Verbesserungen in StockSharp dienen.
- Audiowarnungen, E-Mails und Beschriftungen auf dem Chart aus der MT4-Version werden absichtlich weggelassen, da es sich um plattformspezifische Funktionen handelt.
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>
/// Momentum-reversal strategy converted from the MetaTrader 4 expert advisor "The Puncher".
/// Trades when Stochastic and RSI simultaneously signal extreme oversold or overbought conditions
/// and manages positions with optional stop-loss, take-profit, break-even and trailing stop rules.
/// </summary>
public class ThePuncherStrategy : Strategy
{
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<int> _stochasticLength;
private readonly StrategyParam<int> _stochasticSignalPeriod;
private readonly StrategyParam<int> _stochasticSmoothingPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _oversoldLevel;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _trailingStopPips;
private readonly StrategyParam<int> _trailingStepPips;
private readonly StrategyParam<int> _breakEvenPips;
private readonly StrategyParam<DataType> _candleType;
private decimal _pipSize;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takeProfitPrice;
private decimal? _lastTrailingReference;
private bool _breakEvenActivated;
/// <summary>
/// Trade volume used for new market orders.
/// </summary>
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
/// <summary>
/// Lookback length of the Stochastic oscillator.
/// </summary>
public int StochasticLength
{
get => _stochasticLength.Value;
set => _stochasticLength.Value = value;
}
/// <summary>
/// Smoothing period applied to %K before the signal line.
/// </summary>
public int StochasticSignalPeriod
{
get => _stochasticSignalPeriod.Value;
set => _stochasticSignalPeriod.Value = value;
}
/// <summary>
/// Smoothing period of the Stochastic signal line (%D).
/// </summary>
public int StochasticSmoothingPeriod
{
get => _stochasticSmoothingPeriod.Value;
set => _stochasticSmoothingPeriod.Value = value;
}
/// <summary>
/// RSI calculation period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Shared oversold threshold for Stochastic and RSI.
/// </summary>
public decimal OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Shared overbought threshold for Stochastic and RSI.
/// </summary>
public decimal OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// Stop-loss distance in pips. Set to zero to disable.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance in pips. Set to zero to disable.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance in pips. Set to zero to disable.
/// </summary>
public int TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum favorable move before the trailing stop is tightened.
/// </summary>
public int TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Profit in pips required to move the stop to break-even.
/// </summary>
public int BreakEvenPips
{
get => _breakEvenPips.Value;
set => _breakEvenPips.Value = value;
}
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public ThePuncherStrategy()
{
_orderVolume = Param(nameof(OrderVolume), 1m)
.SetDisplay("Order Volume", "Default volume for new entries", "Trading")
.SetGreaterThanZero();
_stochasticLength = Param(nameof(StochasticLength), 100)
.SetDisplay("Stochastic Length", "Lookback period for %K", "Indicators")
.SetGreaterThanZero()
.SetOptimize(50, 150, 10);
_stochasticSignalPeriod = Param(nameof(StochasticSignalPeriod), 3)
.SetDisplay("Stochastic Signal", "Smoothing period for %K", "Indicators")
.SetGreaterThanZero()
.SetOptimize(1, 10, 1);
_stochasticSmoothingPeriod = Param(nameof(StochasticSmoothingPeriod), 3)
.SetDisplay("Stochastic %D", "Smoothing period for %D", "Indicators")
.SetGreaterThanZero()
.SetOptimize(1, 10, 1);
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "Calculation period for RSI", "Indicators")
.SetGreaterThanZero()
.SetOptimize(7, 28, 1);
_oversoldLevel = Param(nameof(OversoldLevel), 30m)
.SetDisplay("Oversold Level", "Shared oversold threshold", "Indicators")
.SetRange(0m, 100m)
.SetOptimize(10m, 40m, 5m);
_overboughtLevel = Param(nameof(OverboughtLevel), 70m)
.SetDisplay("Overbought Level", "Shared overbought threshold", "Indicators")
.SetRange(0m, 100m)
.SetOptimize(60m, 90m, 5m);
_stopLossPips = Param(nameof(StopLossPips), 2000)
.SetDisplay("Stop-Loss (pips)", "Protective stop distance", "Risk")
.SetOptimize(200, 3000, 200);
_takeProfitPips = Param(nameof(TakeProfitPips), 0)
.SetDisplay("Take-Profit (pips)", "Profit target distance", "Risk")
.SetOptimize(0, 3000, 200);
_trailingStopPips = Param(nameof(TrailingStopPips), 0)
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance", "Risk")
.SetOptimize(0, 2000, 100);
_trailingStepPips = Param(nameof(TrailingStepPips), 1)
.SetDisplay("Trailing Step (pips)", "Minimum move before trailing", "Risk")
.SetNotNegative()
.SetOptimize(0, 500, 10);
_breakEvenPips = Param(nameof(BreakEvenPips), 0)
.SetDisplay("Break-Even (pips)", "Profit required to move stop to entry", "Risk")
.SetNotNegative()
.SetOptimize(0, 1000, 50);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe for signals", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetTradeState();
_pipSize = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = OrderVolume;
_pipSize = CalculatePipSize();
var stochastic = new StochasticOscillator();
stochastic.K.Length = StochasticLength;
stochastic.D.Length = StochasticSmoothingPeriod;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(stochastic, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, stochastic);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochasticValue, IIndicatorValue rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!stochasticValue.IsFinal || !rsiValue.IsFinal)
return;
var stochastic = (StochasticOscillatorValue)stochasticValue;
if (stochastic.D is not decimal stochSignal)
return;
var rsi = rsiValue.ToDecimal();
UpdateEntryPriceFromPosition();
var buySignal = stochSignal < OversoldLevel && rsi < OversoldLevel;
var sellSignal = stochSignal > OverboughtLevel && rsi > OverboughtLevel;
if (HandleActivePosition(candle, buySignal, sellSignal))
return;
if (Position == 0)
{
if (buySignal)
{
EnterLong(candle.ClosePrice);
}
else if (sellSignal)
{
EnterShort(candle.ClosePrice);
}
}
}
private bool HandleActivePosition(ICandleMessage candle, bool buySignal, bool sellSignal)
{
if (Position > 0)
{
if (TryExitLongByProtection(candle))
return true;
ApplyLongRiskManagement(candle);
if (sellSignal)
{
SellMarket(Position);
ResetTradeState();
return true;
}
}
else if (Position < 0)
{
if (TryExitShortByProtection(candle))
return true;
ApplyShortRiskManagement(candle);
if (buySignal)
{
BuyMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
}
return false;
}
private bool TryExitLongByProtection(ICandleMessage candle)
{
if (Position <= 0)
return false;
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket(Position);
ResetTradeState();
return true;
}
if (_takeProfitPrice.HasValue && candle.HighPrice >= _takeProfitPrice.Value)
{
SellMarket(Position);
ResetTradeState();
return true;
}
return false;
}
private bool TryExitShortByProtection(ICandleMessage candle)
{
if (Position >= 0)
return false;
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
if (_takeProfitPrice.HasValue && candle.LowPrice <= _takeProfitPrice.Value)
{
BuyMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
return false;
}
private void ApplyLongRiskManagement(ICandleMessage candle)
{
var close = candle.ClosePrice;
if (_pipSize > 0m && BreakEvenPips > 0 && !_breakEvenActivated && _entryPrice > 0m)
{
var breakEvenDistance = BreakEvenPips * _pipSize;
if (close - _entryPrice >= breakEvenDistance)
{
var breakEvenPrice = _entryPrice;
if (!_stopPrice.HasValue || _stopPrice.Value < breakEvenPrice)
_stopPrice = breakEvenPrice;
_breakEvenActivated = true;
}
}
if (_pipSize > 0m && TrailingStopPips > 0)
{
var trailingDistance = TrailingStopPips * _pipSize;
var trailingStep = TrailingStepPips * _pipSize;
var reference = _lastTrailingReference ?? _entryPrice;
var shouldUpdate = trailingStep <= 0m || close - reference >= trailingStep;
if (shouldUpdate && close - _entryPrice > trailingDistance)
{
var newStop = close - trailingDistance;
if (!_stopPrice.HasValue || newStop > _stopPrice.Value)
_stopPrice = newStop;
_lastTrailingReference = close;
}
}
}
private void ApplyShortRiskManagement(ICandleMessage candle)
{
var close = candle.ClosePrice;
if (_pipSize > 0m && BreakEvenPips > 0 && !_breakEvenActivated && _entryPrice > 0m)
{
var breakEvenDistance = BreakEvenPips * _pipSize;
if (_entryPrice - close >= breakEvenDistance)
{
var breakEvenPrice = _entryPrice;
if (!_stopPrice.HasValue || _stopPrice.Value > breakEvenPrice)
_stopPrice = breakEvenPrice;
_breakEvenActivated = true;
}
}
if (_pipSize > 0m && TrailingStopPips > 0)
{
var trailingDistance = TrailingStopPips * _pipSize;
var trailingStep = TrailingStepPips * _pipSize;
var reference = _lastTrailingReference ?? _entryPrice;
var shouldUpdate = trailingStep <= 0m || reference - close >= trailingStep;
if (shouldUpdate && _entryPrice - close > trailingDistance)
{
var newStop = close + trailingDistance;
if (!_stopPrice.HasValue || newStop < _stopPrice.Value)
_stopPrice = newStop;
_lastTrailingReference = close;
}
}
}
private void EnterLong(decimal referencePrice)
{
BuyMarket();
_entryPrice = referencePrice;
InitializeProtectionLevels(isLong: true);
}
private void EnterShort(decimal referencePrice)
{
SellMarket();
_entryPrice = referencePrice;
InitializeProtectionLevels(isLong: false);
}
private void InitializeProtectionLevels(bool isLong)
{
_stopPrice = null;
_takeProfitPrice = null;
_lastTrailingReference = null;
_breakEvenActivated = false;
if (_pipSize <= 0m)
return;
var stopDistance = StopLossPips > 0 ? StopLossPips * _pipSize : 0m;
var takeDistance = TakeProfitPips > 0 ? TakeProfitPips * _pipSize : 0m;
if (isLong)
{
if (stopDistance > 0m)
_stopPrice = _entryPrice - stopDistance;
if (takeDistance > 0m)
_takeProfitPrice = _entryPrice + takeDistance;
}
else
{
if (stopDistance > 0m)
_stopPrice = _entryPrice + stopDistance;
if (takeDistance > 0m)
_takeProfitPrice = _entryPrice - takeDistance;
}
}
private void UpdateEntryPriceFromPosition()
{
// Entry price is tracked manually via _entryPrice field
}
private void ResetTradeState()
{
_entryPrice = 0m;
_stopPrice = null;
_takeProfitPrice = null;
_lastTrailingReference = null;
_breakEvenActivated = false;
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step > 0m)
return step;
var decimals = Security?.Decimals ?? 0;
if (decimals > 0)
{
var value = 1m;
for (var i = 0; i < decimals; i++)
value /= 10m;
return value;
}
return 0.0001m;
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class the_puncher_strategy(Strategy):
"""Stochastic + RSI extreme oversold/overbought with SL/TP/trailing/break-even."""
def __init__(self):
super(the_puncher_strategy, self).__init__()
self._stoch_length = self.Param("StochasticLength", 100).SetGreaterThanZero().SetDisplay("Stochastic Length", "Lookback for %K", "Indicators")
self._stoch_d = self.Param("StochasticSmoothingPeriod", 3).SetGreaterThanZero().SetDisplay("Stochastic %D", "Smoothing for %D", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14).SetGreaterThanZero().SetDisplay("RSI Period", "RSI period", "Indicators")
self._oversold = self.Param("OversoldLevel", 30.0).SetDisplay("Oversold Level", "Shared oversold threshold", "Indicators")
self._overbought = self.Param("OverboughtLevel", 70.0).SetDisplay("Overbought Level", "Shared overbought threshold", "Indicators")
self._sl_pips = self.Param("StopLossPips", 2000).SetDisplay("Stop-Loss (pips)", "Protective stop distance", "Risk")
self._tp_pips = self.Param("TakeProfitPips", 0).SetDisplay("Take-Profit (pips)", "Profit target distance", "Risk")
self._trail_pips = self.Param("TrailingStopPips", 0).SetDisplay("Trailing Stop (pips)", "Trailing stop distance", "Risk")
self._trail_step = self.Param("TrailingStepPips", 1).SetNotNegative().SetDisplay("Trailing Step (pips)", "Min move before trailing", "Risk")
self._be_pips = self.Param("BreakEvenPips", 0).SetNotNegative().SetDisplay("Break-Even (pips)", "Profit to move stop to entry", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Primary timeframe", "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_puncher_strategy, self).OnReseted()
self._entry_price = 0
self._stop_price = 0
self._tp_price = 0
self._be_activated = False
def OnStarted2(self, time):
super(the_puncher_strategy, self).OnStarted2(time)
self._entry_price = 0
self._stop_price = 0
self._tp_price = 0
self._be_activated = False
self._stoch = StochasticOscillator()
self._stoch.K.Length = self._stoch_length.Value
self._stoch.D.Length = self._stoch_d.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(self._stoch, rsi, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, self._stoch)
self.DrawOwnTrades(area)
def OnProcess(self, candle, stoch_val, rsi_val):
if candle.State != CandleStates.Finished:
return
# Extract %D from stochastic
stoch_signal = None
inner = stoch_val.InnerValues
if inner is not None:
for iv in inner:
stoch_signal = float(iv.Value)
break
if stoch_signal is None:
return
rsi = float(rsi_val)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
oversold = self._oversold.Value
overbought = self._overbought.Value
buy_signal = stoch_signal < oversold and rsi < oversold
sell_signal = stoch_signal > overbought and rsi > overbought
# Manage position
if self.Position > 0:
if self._stop_price > 0 and low <= self._stop_price:
self.SellMarket()
self._reset_state()
return
if self._tp_price > 0 and high >= self._tp_price:
self.SellMarket()
self._reset_state()
return
self._apply_long_risk(close)
if sell_signal:
self.SellMarket()
self._reset_state()
return
elif self.Position < 0:
if self._stop_price > 0 and high >= self._stop_price:
self.BuyMarket()
self._reset_state()
return
if self._tp_price > 0 and low <= self._tp_price:
self.BuyMarket()
self._reset_state()
return
self._apply_short_risk(close)
if buy_signal:
self.BuyMarket()
self._reset_state()
return
# Entry
if self.Position == 0:
if buy_signal:
self.BuyMarket()
self._entry_price = close
self._init_protection(True)
elif sell_signal:
self.SellMarket()
self._entry_price = close
self._init_protection(False)
def _init_protection(self, is_long):
self._stop_price = 0
self._tp_price = 0
self._be_activated = False
sl = self._sl_pips.Value
tp = self._tp_pips.Value
if is_long:
if sl > 0:
self._stop_price = self._entry_price - sl
if tp > 0:
self._tp_price = self._entry_price + tp
else:
if sl > 0:
self._stop_price = self._entry_price + sl
if tp > 0:
self._tp_price = self._entry_price - tp
def _apply_long_risk(self, close):
be = self._be_pips.Value
if be > 0 and not self._be_activated and self._entry_price > 0:
if close - self._entry_price >= be:
if self._stop_price == 0 or self._stop_price < self._entry_price:
self._stop_price = self._entry_price
self._be_activated = True
trail = self._trail_pips.Value
step = self._trail_step.Value
if trail > 0 and close - self._entry_price > trail:
new_stop = close - trail
if self._stop_price == 0 or new_stop > self._stop_price:
self._stop_price = new_stop
def _apply_short_risk(self, close):
be = self._be_pips.Value
if be > 0 and not self._be_activated and self._entry_price > 0:
if self._entry_price - close >= be:
if self._stop_price == 0 or self._stop_price > self._entry_price:
self._stop_price = self._entry_price
self._be_activated = True
trail = self._trail_pips.Value
step = self._trail_step.Value
if trail > 0 and self._entry_price - close > trail:
new_stop = close + trail
if self._stop_price == 0 or new_stop < self._stop_price:
self._stop_price = new_stop
def _reset_state(self):
self._entry_price = 0
self._stop_price = 0
self._tp_price = 0
self._be_activated = False
def CreateClone(self):
return the_puncher_strategy()