Daytrading-Impulsstrategie
Überblick
Die DayTrading-Strategie ist eine originalgetreue C#-Konvertierung des klassischen MetaTrader 4-Expertenberaters „DayTrading“, der 2005 von NazFunds veröffentlicht wurde. Der ursprüngliche Roboter wurde für 5-Minuten-Forex-Charts entwickelt und kombiniert mehrere Momentum- und Trendfolgeindikatoren, um kurzfristige Richtungsbewegungen mit einem bescheidenen festen Ziel und optionalem Trailing Stop zu erfassen. Diese StockSharp-Implementierung reproduziert die Kernentscheidungslogik und stellt gleichzeitig jeden wichtigen Schwellenwert als Strategieparameter bereit, sodass er optimiert oder an verschiedene Instrumente angepasst werden kann.
Indikatorstapel
Die Strategie bewertet vier Indikatoren für die ausgewählte Kerzenserie:
- Parabolic SAR (
ParabolicSar) mit konfigurierbarer Beschleunigung, Inkrement und Obergrenze. Es definiert die grundlegende Trendrichtung und muss unter/über dem Preis spiegeln, um neue Einträge zu ermöglichen.
- MACD (12, 26, 9) (
MovingAverageConvergenceDivergenceSignal). Die MACD-Linie muss bei Long-Positionen unter der Signallinie und bei Short-Positionen darüber liegen, was den ursprünglichen Histogramm-/Signal-Vergleich in MQL widerspiegelt.
- Stochastic Oszillator (5, 3, 3) (
StochasticOscillator). Die %K-Linie muss bei Long-Positionen unter 35 und bei Short-Positionen über 60 bleiben, um sicherzustellen, dass der Markt aus einer überverkauften/überkauften Zone herauskommt.
- Impuls (14) (
Momentum). Ein Wert unter 100 schaltet Long-Trades frei, während ein Wert über 100 Short-Trades zulässt, genau wie im MT4-Skript.
Alle Indikatoren werden über die übergeordnete BindEx-Pipeline verarbeitet, sodass keine manuelle Pufferverwaltung oder historische Indexierung erforderlich ist.
Handelsregeln
Teilnahmebedingungen
Eine Long-Position wird eröffnet, wenn bei der zuletzt abgeschlossenen Kerze alle folgenden Bedingungen zutreffen:
- Der Parabolic SAR-Punkt liegt auf oder unter dem aktuellen Briefkurs und der vorherige Punkt lag über dem aktuellen Punkt (frischer SAR-Umschlag in bullisch).
- Momentum liegt unter 100.
- Die MACD-Linie liegt unterhalb ihrer Signallinie.
- Stochastic %K liegt unter 35.
Eine Short-Position wird eröffnet, wenn die symmetrischen Bedingungen erfüllt sind:
- Der Punkt Parabolic SAR wird auf oder über dem aktuellen Geldkurs gedruckt, und der vorherige Punkt lag unter dem aktuellen Punkt (bearish flip).
- Das Momentum liegt über 100.
- Die MACD-Linie liegt über ihrer Signallinie.
- Stochastic %K liegt über 60.
Es kann jeweils nur eine Position offen sein. Immer wenn ein entgegengesetztes Signal auftritt, wird die bestehende Position geschlossen und es erfolgt kein erneuter Einstieg bei derselben Kerze – genau wie in der MetaTrader-Implementierung, wo der OrdersTotal-Scan ein sofortiges Neuladen verhindert.
Exit-Management
- Stop-Loss / Take-Profit: Optionale feste Distanzen (in Punkten) werden anhand der Tick-Größe des Instruments in absolute Preise umgewandelt. Sie werden bei jeder Kerze neu bewertet und schließen die Position, wenn sie intrabar überschritten werden.
- Trailing Stop: Sobald der Preis um die konfigurierte Anzahl von Punkten steigt, wird ein Trailing Stop aktiviert. Bei Long-Trades liegt der Stop unterhalb des Schlusskurses; Bei Short-Trades bleibt es über dem Schlusskurs. Der Stopp geht nie zurück, sodass der Gewinn schrittweise gesperrt wird.
- Gegensignal: Ein gültiges Gegensignal löst die aktuelle Position sofort auf, bevor ein neuer Eintrag berücksichtigt wird.
Es wird keine zusätzliche Raster-, Skalierungs- oder Absicherungslogik hinzugefügt. Die Strategie bleibt so leichtgewichtig und deterministisch wie das Original EA.
Parameter
| Parameter |
Standard |
Beschreibung |
LotSize |
1 |
Volumen jeder Marktorder. Die Eigenschaft Strategy.Volume wird beim Start mit diesem Wert synchronisiert. |
TrailingStopPoints |
15 |
Nachlaufdistanz in Punkten. Auf Null setzen, um das Nachziehen zu deaktivieren. |
TakeProfitPoints |
20 |
Feste Take-Profit-Distanz in Punkten. Auf Null setzen, um das Ziel zu entfernen. |
StopLossPoints |
0 |
Schutzstoppabstand in Punkten. Zero reproduziert das ursprüngliche „No Stop“-Verhalten. |
SlippagePoints |
3 |
Platzhalter für maximale Ausführungsschlupf (zur Kompatibilität mit der MT4-Eingabe). Wird nicht automatisch erzwungen, wird aber der Vollständigkeit halber beibehalten. |
CandleType |
Zeitrahmen von 5 Minuten |
Von allen Indikatoren verwendete Kerzenserie. Bleiben Sie bei M5, um der ursprünglichen Empfehlung von EA zu entsprechen. |
MacdFastPeriod |
12 |
Schnelle EMA-Länge in der MACD-Berechnung. |
MacdSlowPeriod |
26 |
Langsame EMA-Länge in der MACD-Berechnung. |
MacdSignalPeriod |
9 |
Signallänge EMA in der MACD-Berechnung. |
StochasticLength |
5 |
%K Lookback-Länge für den Stochastic-Oszillator. |
StochasticSignal |
3 |
%D Glättungslänge. |
StochasticSlow |
3 |
Zusätzliche Verlangsamung auf der %K-Linie. |
MomentumPeriod |
14 |
Momentum-Lookback-Länge. |
SarAcceleration |
0,02 |
Anfänglicher Beschleunigungsfaktor für Parabolic SAR. |
SarStep |
0,02 |
Auf den Beschleunigungsfaktor angewendetes Inkrement nach jedem neuen Extremwert. |
SarMaximum |
0,2 |
Maximaler Beschleunigungsfaktor für Parabolic SAR. |
Alle numerischen Parameter können dank der SetCanOptimize(true)-Hinweise durch den Optimierungsworkflow von StockSharp optimiert werden.
Implementierungshinweise
- Geld-/Briefpreise werden aus Live-Level1-Daten abgeleitet, sofern verfügbar; andernfalls fungiert der Kerzenschluss als Fallback, so dass die Logik bei historischen Tests robust bleibt.
- Die Punkteumrechnung hängt vom
Step/PriceStep des Instruments ab. Wenn keine Angabe erfolgt, wird ein konservativer 0.0001-Fallback verwendet, der einem Standard-Forex-Pip entspricht.
- Das Positionsmanagement spiegelt den MT4 EA wider: Die Strategie ist nie eine Pyramide und hält nie beide Richtungen gleichzeitig.
- Kommentare im Code sind gemäß den Projektrichtlinien auf Englisch, während diese README-Datei eine erweiterte Dokumentation für ein einfacheres Onboarding enthält.
Nutzungstipps
- Weisen Sie der Strategie das gewünschte Forex-Paar zu, belassen Sie den Kerzentyp bei 5 Minuten und starten Sie die Strategie. Die Anzeigen werden automatisch aufgewärmt.
- Erwägen Sie die Aktivierung eines Stop-Loss ungleich Null, wenn Sie Live-Daten verwenden. Das ursprüngliche Skript empfahl den Handel ohne Stop-Loss, aber Trailing-Stops allein reichen möglicherweise nicht zur Risikokontrolle aus.
- Für algorithmische Portfolios können Sie diese Strategie zu einem
BasketStrategy hinzufügen und die Kapitalallokation extern verwalten, während Sie weiterhin von den offengelegten Parametern zur Optimierung profitieren.
Diese Dokumentation sorgt zusammen mit den russischen und chinesischen Übersetzungen im selben Ordner für vollständige Transparenz der konvertierten Logik.
namespace StockSharp.Samples.Strategies;
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;
/// <summary>
/// Intraday trend strategy converted from the MetaTrader "DayTrading" expert advisor.
/// Combines Parabolic SAR, MACD, Stochastic and Momentum filters with trailing exits.
/// </summary>
public class DayTradingImpulseStrategy : Strategy
{
private readonly StrategyParam<decimal> _lotSize;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _slippagePoints;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _macdFastPeriod;
private readonly StrategyParam<int> _macdSlowPeriod;
private readonly StrategyParam<int> _macdSignalPeriod;
private readonly StrategyParam<int> _stochasticLength;
private readonly StrategyParam<int> _stochasticSignal;
private readonly StrategyParam<int> _stochasticSlow;
private readonly StrategyParam<decimal> _stochasticBuyThreshold;
private readonly StrategyParam<decimal> _stochasticSellThreshold;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<decimal> _momentumNeutralLevel;
private readonly StrategyParam<decimal> _sarAcceleration;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMaximum;
private ParabolicSar _parabolicSar = null!;
private MovingAverageConvergenceDivergenceSignal _macd = null!;
private StochasticOscillator _stochastic = null!;
private Momentum _momentum = null!;
private decimal? _previousSar;
private decimal? _longStopPrice;
private decimal? _shortStopPrice;
private decimal? _longTakeProfit;
private decimal? _shortTakeProfit;
private decimal? _longEntryPrice;
private decimal? _shortEntryPrice;
private decimal _pointSize;
/// <summary>
/// Initializes a new instance of <see cref="DayTradingImpulseStrategy"/>.
/// </summary>
public DayTradingImpulseStrategy()
{
_lotSize = Param(nameof(LotSize), 1m)
.SetGreaterThanZero()
.SetDisplay("Order Volume", "Trade volume used for each market entry", "Trading")
;
_trailingStopPoints = Param(nameof(TrailingStopPoints), 15m)
.SetNotNegative()
.SetDisplay("Trailing Stop (points)", "Distance used to trail profitable positions", "Risk")
;
_takeProfitPoints = Param(nameof(TakeProfitPoints), 20m)
.SetNotNegative()
.SetDisplay("Take Profit (points)", "Fixed profit target measured in points", "Risk")
;
_stopLossPoints = Param(nameof(StopLossPoints), 0m)
.SetNotNegative()
.SetDisplay("Stop Loss (points)", "Protective stop distance measured in points", "Risk")
;
_slippagePoints = Param(nameof(SlippagePoints), 3m)
.SetNotNegative()
.SetDisplay("Slippage (points)", "Maximum acceptable execution slippage", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Time frame used for indicator calculations", "Data");
_macdFastPeriod = Param(nameof(MacdFastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Length of the fast EMA in MACD", "Indicators")
;
_macdSlowPeriod = Param(nameof(MacdSlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Length of the slow EMA in MACD", "Indicators")
;
_macdSignalPeriod = Param(nameof(MacdSignalPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Length of the MACD signal EMA", "Indicators")
;
_stochasticLength = Param(nameof(StochasticLength), 5)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
;
_stochasticSignal = Param(nameof(StochasticSignal), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Period of the %D smoothing", "Indicators")
;
_stochasticSlow = Param(nameof(StochasticSlow), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic Slowing", "Final smoothing applied to %K", "Indicators")
;
_stochasticBuyThreshold = Param(nameof(StochasticBuyThreshold), 35m)
.SetDisplay("Stochastic Buy", "Oversold %K threshold for long entries", "Indicators")
;
_stochasticSellThreshold = Param(nameof(StochasticSellThreshold), 60m)
.SetDisplay("Stochastic Sell", "Overbought %K threshold for short entries", "Indicators")
;
_momentumPeriod = Param(nameof(MomentumPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Number of candles used for Momentum", "Indicators")
;
_momentumNeutralLevel = Param(nameof(MomentumNeutralLevel), 100m)
.SetDisplay("Momentum Neutral", "Neutral momentum value used for signal confirmation", "Indicators")
;
_sarAcceleration = Param(nameof(SarAcceleration), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Acceleration", "Initial acceleration factor of Parabolic SAR", "Indicators")
;
_sarStep = Param(nameof(SarStep), 0.02m)
.SetGreaterThanZero()
.SetDisplay("SAR Step", "Increment applied to the acceleration factor", "Indicators")
;
_sarMaximum = Param(nameof(SarMaximum), 0.2m)
.SetGreaterThanZero()
.SetDisplay("SAR Maximum", "Maximum acceleration factor of Parabolic SAR", "Indicators")
;
}
/// <summary>
/// Trade volume used for each market entry.
/// </summary>
public decimal LotSize
{
get => _lotSize.Value;
set => _lotSize.Value = value;
}
/// <summary>
/// Distance used to trail profitable positions.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Fixed profit target measured in points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Protective stop distance measured in points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Maximum acceptable execution slippage.
/// </summary>
public decimal SlippagePoints
{
get => _slippagePoints.Value;
set => _slippagePoints.Value = value;
}
/// <summary>
/// Time frame used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the fast EMA in MACD.
/// </summary>
public int MacdFastPeriod
{
get => _macdFastPeriod.Value;
set => _macdFastPeriod.Value = value;
}
/// <summary>
/// Length of the slow EMA in MACD.
/// </summary>
public int MacdSlowPeriod
{
get => _macdSlowPeriod.Value;
set => _macdSlowPeriod.Value = value;
}
/// <summary>
/// Length of the MACD signal EMA.
/// </summary>
public int MacdSignalPeriod
{
get => _macdSignalPeriod.Value;
set => _macdSignalPeriod.Value = value;
}
/// <summary>
/// Period of the %K line.
/// </summary>
public int StochasticLength
{
get => _stochasticLength.Value;
set => _stochasticLength.Value = value;
}
/// <summary>
/// Period of the %D smoothing.
/// </summary>
public int StochasticSignal
{
get => _stochasticSignal.Value;
set => _stochasticSignal.Value = value;
}
/// <summary>
/// Final smoothing applied to %K.
/// </summary>
public int StochasticSlow
{
get => _stochasticSlow.Value;
set => _stochasticSlow.Value = value;
}
/// <summary>
/// Stochastic %K level that qualifies oversold conditions.
/// </summary>
public decimal StochasticBuyThreshold
{
get => _stochasticBuyThreshold.Value;
set => _stochasticBuyThreshold.Value = value;
}
/// <summary>
/// Stochastic %K level that qualifies overbought conditions.
/// </summary>
public decimal StochasticSellThreshold
{
get => _stochasticSellThreshold.Value;
set => _stochasticSellThreshold.Value = value;
}
/// <summary>
/// Number of candles used for Momentum.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Momentum value considered neutral for trend confirmation.
/// </summary>
public decimal MomentumNeutralLevel
{
get => _momentumNeutralLevel.Value;
set => _momentumNeutralLevel.Value = value;
}
/// <summary>
/// Initial acceleration factor of Parabolic SAR.
/// </summary>
public decimal SarAcceleration
{
get => _sarAcceleration.Value;
set => _sarAcceleration.Value = value;
}
/// <summary>
/// Increment applied to the acceleration factor.
/// </summary>
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
/// <summary>
/// Maximum acceleration factor of Parabolic SAR.
/// </summary>
public decimal SarMaximum
{
get => _sarMaximum.Value;
set => _sarMaximum.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousSar = null;
_longStopPrice = null;
_shortStopPrice = null;
_longTakeProfit = null;
_shortTakeProfit = null;
_longEntryPrice = null;
_shortEntryPrice = null;
_pointSize = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = LotSize;
_pointSize = CalculatePointSize();
_parabolicSar = new ParabolicSar
{
Acceleration = SarAcceleration,
AccelerationStep = SarStep,
AccelerationMax = SarMaximum,
};
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastPeriod },
LongMa = { Length = MacdSlowPeriod },
},
SignalMa = { Length = MacdSignalPeriod },
};
_stochastic = new StochasticOscillator();
_stochastic.K.Length = StochasticLength;
_stochastic.D.Length = StochasticSignal;
_momentum = new Momentum
{
Length = MomentumPeriod,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_parabolicSar, _macd, _stochastic, _momentum, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _parabolicSar);
DrawIndicator(area, _macd);
DrawIndicator(area, _stochastic);
DrawIndicator(area, _momentum);
DrawOwnTrades(area);
}
}
private void ProcessCandle(
ICandleMessage candle,
IIndicatorValue sarValue,
IIndicatorValue macdValue,
IIndicatorValue stochasticValue,
IIndicatorValue momentumValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!sarValue.IsFinal || !macdValue.IsFinal || !stochasticValue.IsFinal || !momentumValue.IsFinal)
return;
if (macdValue is not MovingAverageConvergenceDivergenceSignalValue macd)
return;
if (stochasticValue is not StochasticOscillatorValue stochastic)
return;
var sar = sarValue.ToDecimal();
var previousSar = _previousSar;
_previousSar = sar;
if (previousSar is null)
return;
var momentum = momentumValue.ToDecimal();
var ask = GetAskPrice(candle);
var bid = GetBidPrice(candle);
var buySignal = sar <= ask && previousSar.Value > sar && momentum < MomentumNeutralLevel &&
macd.Macd < macd.Signal && stochastic.K < StochasticBuyThreshold;
var sellSignal = sar >= bid && previousSar.Value < sar && momentum > MomentumNeutralLevel &&
macd.Macd > macd.Signal && stochastic.K > StochasticSellThreshold;
var closedPosition = false;
if (Position > 0)
{
if (sellSignal)
{
SellMarket(Math.Abs(Position));
ResetLongState();
closedPosition = true;
}
else if (HandleLongRisk(candle))
{
closedPosition = true;
}
}
else if (Position < 0)
{
if (buySignal)
{
BuyMarket(Math.Abs(Position));
ResetShortState();
closedPosition = true;
}
else if (HandleShortRisk(candle))
{
closedPosition = true;
}
}
if (closedPosition)
return;
if (Position == 0)
{
if (buySignal)
{
var entryPrice = ask;
BuyMarket(Volume);
_longEntryPrice = entryPrice;
_longStopPrice = StopLossPoints > 0m ? entryPrice - ConvertPoints(StopLossPoints) : null;
_longTakeProfit = TakeProfitPoints > 0m ? entryPrice + ConvertPoints(TakeProfitPoints) : null;
}
else if (sellSignal)
{
var entryPrice = bid;
SellMarket(Volume);
_shortEntryPrice = entryPrice;
_shortStopPrice = StopLossPoints > 0m ? entryPrice + ConvertPoints(StopLossPoints) : null;
_shortTakeProfit = TakeProfitPoints > 0m ? entryPrice - ConvertPoints(TakeProfitPoints) : null;
}
}
}
private bool HandleLongRisk(ICandleMessage candle)
{
if (Math.Abs(Position) <= 0m)
return false;
if (_longTakeProfit is decimal takeProfit && candle.HighPrice >= takeProfit)
{
SellMarket(Math.Abs(Position));
ResetLongState();
return true;
}
if (_longStopPrice is decimal stop && candle.LowPrice <= stop)
{
SellMarket(Math.Abs(Position));
ResetLongState();
return true;
}
var trailingDistance = ConvertPoints(TrailingStopPoints);
if (trailingDistance > 0m && _longEntryPrice is decimal entry)
{
var progressed = candle.HighPrice - entry;
if (progressed >= trailingDistance)
{
var candidate = candle.ClosePrice - trailingDistance;
if (!_longStopPrice.HasValue || candidate > _longStopPrice.Value)
_longStopPrice = candidate;
}
}
return false;
}
private bool HandleShortRisk(ICandleMessage candle)
{
if (Math.Abs(Position) <= 0m)
return false;
if (_shortTakeProfit is decimal takeProfit && candle.LowPrice <= takeProfit)
{
BuyMarket(Math.Abs(Position));
ResetShortState();
return true;
}
if (_shortStopPrice is decimal stop && candle.HighPrice >= stop)
{
BuyMarket(Math.Abs(Position));
ResetShortState();
return true;
}
var trailingDistance = ConvertPoints(TrailingStopPoints);
if (trailingDistance > 0m && _shortEntryPrice is decimal entry)
{
var progressed = entry - candle.LowPrice;
if (progressed >= trailingDistance)
{
var candidate = candle.ClosePrice + trailingDistance;
if (!_shortStopPrice.HasValue || candidate < _shortStopPrice.Value)
_shortStopPrice = candidate;
}
}
return false;
}
private void ResetLongState()
{
_longEntryPrice = null;
_longStopPrice = null;
_longTakeProfit = null;
}
private void ResetShortState()
{
_shortEntryPrice = null;
_shortStopPrice = null;
_shortTakeProfit = null;
}
private decimal GetBidPrice(ICandleMessage candle)
{
return candle.ClosePrice;
}
private decimal GetAskPrice(ICandleMessage candle)
{
return candle.ClosePrice;
}
private decimal ConvertPoints(decimal points)
{
if (points <= 0m)
return 0m;
if (_pointSize > 0m)
return points * _pointSize;
var step = Security?.PriceStep ?? 0m;
return step > 0m ? points * step : points;
}
private decimal CalculatePointSize()
{
var step = Security?.PriceStep ?? 0m;
return step > 0m ? step : 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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import (
ParabolicSar,
MovingAverageConvergenceDivergenceSignal,
StochasticOscillator,
Momentum,
)
class day_trading_impulse_strategy(Strategy):
def __init__(self):
super(day_trading_impulse_strategy, self).__init__()
self._lot_size = self.Param("LotSize", 1.0) \
.SetDisplay("Order Volume", "Trade volume used for each market entry", "Trading")
self._trailing_stop_points = self.Param("TrailingStopPoints", 15.0) \
.SetDisplay("Trailing Stop (points)", "Distance used to trail profitable positions", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 20.0) \
.SetDisplay("Take Profit (points)", "Fixed profit target measured in points", "Risk")
self._stop_loss_points = self.Param("StopLossPoints", 0.0) \
.SetDisplay("Stop Loss (points)", "Protective stop distance measured in points", "Risk")
self._slippage_points = self.Param("SlippagePoints", 3.0) \
.SetDisplay("Slippage (points)", "Maximum acceptable execution slippage", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Time frame used for indicator calculations", "Data")
self._macd_fast_period = self.Param("MacdFastPeriod", 12) \
.SetDisplay("MACD Fast", "Length of the fast EMA in MACD", "Indicators")
self._macd_slow_period = self.Param("MacdSlowPeriod", 26) \
.SetDisplay("MACD Slow", "Length of the slow EMA in MACD", "Indicators")
self._macd_signal_period = self.Param("MacdSignalPeriod", 9) \
.SetDisplay("MACD Signal", "Length of the MACD signal EMA", "Indicators")
self._stochastic_length = self.Param("StochasticLength", 5) \
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
self._stochastic_signal = self.Param("StochasticSignal", 3) \
.SetDisplay("Stochastic %D", "Period of the %D smoothing", "Indicators")
self._stochastic_slow = self.Param("StochasticSlow", 3) \
.SetDisplay("Stochastic Slowing", "Final smoothing applied to %K", "Indicators")
self._stochastic_buy_threshold = self.Param("StochasticBuyThreshold", 35.0) \
.SetDisplay("Stochastic Buy", "Oversold %K threshold for long entries", "Indicators")
self._stochastic_sell_threshold = self.Param("StochasticSellThreshold", 60.0) \
.SetDisplay("Stochastic Sell", "Overbought %K threshold for short entries", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 14) \
.SetDisplay("Momentum Period", "Number of candles used for Momentum", "Indicators")
self._momentum_neutral_level = self.Param("MomentumNeutralLevel", 100.0) \
.SetDisplay("Momentum Neutral", "Neutral momentum value used for signal confirmation", "Indicators")
self._sar_acceleration = self.Param("SarAcceleration", 0.02) \
.SetDisplay("SAR Acceleration", "Initial acceleration factor of Parabolic SAR", "Indicators")
self._sar_step = self.Param("SarStep", 0.02) \
.SetDisplay("SAR Step", "Increment applied to the acceleration factor", "Indicators")
self._sar_maximum = self.Param("SarMaximum", 0.2) \
.SetDisplay("SAR Maximum", "Maximum acceleration factor of Parabolic SAR", "Indicators")
self._parabolic_sar = None
self._macd = None
self._stochastic = None
self._momentum = None
self._previous_sar = None
self._long_stop_price = None
self._short_stop_price = None
self._long_take_profit = None
self._short_take_profit = None
self._long_entry_price = None
self._short_entry_price = None
self._point_size = 0.0
@property
def LotSize(self):
return self._lot_size.Value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def SlippagePoints(self):
return self._slippage_points.Value
@property
def CandleType(self):
return self._candle_type.Value
@property
def MacdFastPeriod(self):
return self._macd_fast_period.Value
@property
def MacdSlowPeriod(self):
return self._macd_slow_period.Value
@property
def MacdSignalPeriod(self):
return self._macd_signal_period.Value
@property
def StochasticLength(self):
return self._stochastic_length.Value
@property
def StochasticSignal(self):
return self._stochastic_signal.Value
@property
def StochasticSlow(self):
return self._stochastic_slow.Value
@property
def StochasticBuyThreshold(self):
return self._stochastic_buy_threshold.Value
@property
def StochasticSellThreshold(self):
return self._stochastic_sell_threshold.Value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
@property
def MomentumNeutralLevel(self):
return self._momentum_neutral_level.Value
@property
def SarAcceleration(self):
return self._sar_acceleration.Value
@property
def SarStep(self):
return self._sar_step.Value
@property
def SarMaximum(self):
return self._sar_maximum.Value
def _calculate_point_size(self):
step = 0.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
return step if step > 0 else 0.0001
def _convert_points(self, points):
pts = float(points)
if pts <= 0:
return 0.0
if self._point_size > 0:
return pts * self._point_size
step = 0.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
return pts * step if step > 0 else pts
def OnStarted2(self, time):
super(day_trading_impulse_strategy, self).OnStarted2(time)
self.Volume = float(self.LotSize)
self._point_size = self._calculate_point_size()
self._parabolic_sar = ParabolicSar()
self._parabolic_sar.Acceleration = float(self.SarAcceleration)
self._parabolic_sar.AccelerationStep = float(self.SarStep)
self._parabolic_sar.AccelerationMax = float(self.SarMaximum)
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self.MacdFastPeriod
self._macd.Macd.LongMa.Length = self.MacdSlowPeriod
self._macd.SignalMa.Length = self.MacdSignalPeriod
self._stochastic = StochasticOscillator()
self._stochastic.K.Length = self.StochasticLength
self._stochastic.D.Length = self.StochasticSignal
self._momentum = Momentum()
self._momentum.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._parabolic_sar, self._macd, self._stochastic, self._momentum, self.ProcessCandle).Start()
def ProcessCandle(self, candle, sar_value, macd_value, stochastic_value, momentum_value):
if candle.State != CandleStates.Finished:
return
if not sar_value.IsFinal or not macd_value.IsFinal or not stochastic_value.IsFinal or not momentum_value.IsFinal:
return
try:
sar = float(sar_value)
except:
sar = float(sar_value.Value)
previous_sar = self._previous_sar
self._previous_sar = sar
if previous_sar is None:
return
try:
mom = float(momentum_value)
except:
mom = float(momentum_value.Value)
close_price = float(candle.ClosePrice)
macd_raw = macd_value.Macd if hasattr(macd_value, 'Macd') else None
signal_raw = macd_value.Signal if hasattr(macd_value, 'Signal') else None
macd_val = float(macd_raw) if macd_raw is not None else 0.0
signal_val = float(signal_raw) if signal_raw is not None else 0.0
stoch_k_raw = stochastic_value.K if hasattr(stochastic_value, 'K') else None
stoch_k = float(stoch_k_raw) if stoch_k_raw is not None else 0.0
mom_neutral = float(self.MomentumNeutralLevel)
stoch_buy = float(self.StochasticBuyThreshold)
stoch_sell = float(self.StochasticSellThreshold)
buy_signal = sar <= close_price and previous_sar > sar and mom < mom_neutral and macd_val < signal_val and stoch_k < stoch_buy
sell_signal = sar >= close_price and previous_sar < sar and mom > mom_neutral and macd_val > signal_val and stoch_k > stoch_sell
closed_position = False
if self.Position > 0:
if sell_signal:
self.SellMarket(Math.Abs(self.Position))
self._reset_long_state()
closed_position = True
elif self._handle_long_risk(candle):
closed_position = True
elif self.Position < 0:
if buy_signal:
self.BuyMarket(Math.Abs(self.Position))
self._reset_short_state()
closed_position = True
elif self._handle_short_risk(candle):
closed_position = True
if closed_position:
return
if self.Position == 0:
if buy_signal:
entry_price = close_price
self.BuyMarket(self.Volume)
self._long_entry_price = entry_price
sl = float(self.StopLossPoints)
tp = float(self.TakeProfitPoints)
self._long_stop_price = entry_price - self._convert_points(sl) if sl > 0 else None
self._long_take_profit = entry_price + self._convert_points(tp) if tp > 0 else None
elif sell_signal:
entry_price = close_price
self.SellMarket(self.Volume)
self._short_entry_price = entry_price
sl = float(self.StopLossPoints)
tp = float(self.TakeProfitPoints)
self._short_stop_price = entry_price + self._convert_points(sl) if sl > 0 else None
self._short_take_profit = entry_price - self._convert_points(tp) if tp > 0 else None
def _handle_long_risk(self, candle):
if Math.Abs(self.Position) <= 0:
return False
if self._long_take_profit is not None and float(candle.HighPrice) >= self._long_take_profit:
self.SellMarket(Math.Abs(self.Position))
self._reset_long_state()
return True
if self._long_stop_price is not None and float(candle.LowPrice) <= self._long_stop_price:
self.SellMarket(Math.Abs(self.Position))
self._reset_long_state()
return True
trailing_distance = self._convert_points(self.TrailingStopPoints)
if trailing_distance > 0 and self._long_entry_price is not None:
progressed = float(candle.HighPrice) - self._long_entry_price
if progressed >= trailing_distance:
candidate = float(candle.ClosePrice) - trailing_distance
if self._long_stop_price is None or candidate > self._long_stop_price:
self._long_stop_price = candidate
return False
def _handle_short_risk(self, candle):
if Math.Abs(self.Position) <= 0:
return False
if self._short_take_profit is not None and float(candle.LowPrice) <= self._short_take_profit:
self.BuyMarket(Math.Abs(self.Position))
self._reset_short_state()
return True
if self._short_stop_price is not None and float(candle.HighPrice) >= self._short_stop_price:
self.BuyMarket(Math.Abs(self.Position))
self._reset_short_state()
return True
trailing_distance = self._convert_points(self.TrailingStopPoints)
if trailing_distance > 0 and self._short_entry_price is not None:
progressed = self._short_entry_price - float(candle.LowPrice)
if progressed >= trailing_distance:
candidate = float(candle.ClosePrice) + trailing_distance
if self._short_stop_price is None or candidate < self._short_stop_price:
self._short_stop_price = candidate
return False
def _reset_long_state(self):
self._long_entry_price = None
self._long_stop_price = None
self._long_take_profit = None
def _reset_short_state(self):
self._short_entry_price = None
self._short_stop_price = None
self._short_take_profit = None
def OnReseted(self):
super(day_trading_impulse_strategy, self).OnReseted()
self._parabolic_sar = None
self._macd = None
self._stochastic = None
self._momentum = None
self._previous_sar = None
self._long_stop_price = None
self._short_stop_price = None
self._long_take_profit = None
self._short_take_profit = None
self._long_entry_price = None
self._short_entry_price = None
self._point_size = 0.0
def CreateClone(self):
return day_trading_impulse_strategy()