Die zweiteinfachste Strategie ist der StockSharp-Port des MetaTrader-Experten Second_Easiest.mq4. Der ursprüngliche Roboter scannt die
Tageskerze der aktuellen Handelssitzung und eröffnet eine einzelne Intraday-Position, sobald der Preis zeigt, dass er von der Kerze abweicht
Tag ist geöffnet. Wenn der Markt schließt, liquidiert der Experte alle Engagements und bereitet sich so auf die nächste Sitzung vor. Der StockSharp
Die Version behält dieses Intraday-Breakout-Verhalten bei und nutzt gleichzeitig die High-Level-API des Frameworks für Kerzen
Abonnements, Auftragsverwaltung und Positionsverfolgung.
Im Gegensatz zu Momentum-Strategien, die mehrere Indikatoren erfordern, benötigt Second Easiest nur die laufende Eröffnung, das Hoch und das Tief der Indikatoren
aktueller Tag. Dadurch ist es sehr leichtgewichtig und reagiert dennoch auf die ersten Anzeichen einer Richtungsüberzeugung. Der Code bleibt erhalten
eine Position nach der anderen und kehrt niemals sofort um; Der neue Handel kann erst eröffnet werden, nachdem der vorherige geschlossen wurde.
Handelslogik
Abonnieren Sie die durch CandleType definierte Intraday-Kerzenserie. Der Standardwert ist ein Zeitrahmen von einer Minute, was einen frühen Zeitpunkt darstellt
Sicht auf Tagesextreme und bleibt gleichzeitig mit der Tageslogik des Originals EA kompatibel.
Aktualisieren Sie für jede fertige Kerze den In-Memory-Datensatz der Eröffnungs-, Höchst- und Tiefstkurse der Sitzung. Die erste Kerze wurde verarbeitet
an einem neuen Handelstag alle drei Werte definiert; Nachfolgende Kerzen erweitern nur das Hoch oder Tief, wenn ein neues Extrem erreicht wird.
Ignorieren Sie neue Setups, sobald die Uhr EntryCutoffHour erreicht. Der MetaTrader-Code stoppt die Eröffnung von Trades um 16:00 Uhr Serverzeit und
Der Hafen folgt der gleichen Regel.
Eine Long-Position ist nur zulässig, wenn der aktuelle Schlusskurs über dem Tageseröffnungskurs liegt und der Abstand zwischen dem Eröffnungskurs und dem Tageseröffnungskurs liegt
Tagestiefstwert übersteigt RangePointsThreshold. Dies reproduziert die Bedingungen „Bid > open“ und „open – low > 15 Punkte“ aus MQL.
Eine Short-Position ist nur zulässig, wenn der aktuelle Schlusskurs unter dem täglichen Eröffnungskurs und dem Abstand zwischen dem Tageshöchststand und dem Tageshöchstkurs liegt
die Öffnung überschreitet den gleichen Schwellenwert.
Wenn ein Einstiegssignal erscheint und keine Position offen ist, senden Sie eine Marktorder mit TradeVolume Lots. Die Hilfsmethoden von
Die Basisklasse Strategy kümmert sich um die Auswahl der richtigen Seite.
Sobald der Markt MarketCloseHour erreicht, glätten Sie das bestehende Risiko, indem Sie ClosePosition() aufrufen. Es werden keine neuen Geschäfte platziert
nach dieser Unterbrechung bis zum Beginn der nächsten Sitzung.
Parameter
Name
Typ
Standard
Beschreibung
CandleType
DataType
Zeitrahmen von 1 Minute
Primäre Intraday-Kerzen steuern die Ein- und Ausstiegslogik.
TradeVolume
decimal
1
Für jede Marktorder verwendete Losgröße.
EntryCutoffHour
int
16
Stunde (0-23), nach der die Strategie die Eröffnung neuer Positionen verweigert.
MarketCloseHour
int
20
Stunde (0-23), wenn eine offene Position zwangsweise geschlossen wird.
RangePointsThreshold
decimal
15
Mindestabstand, ausgedrückt in Broker-Punkten, zwischen der Tageseröffnung und dem nächsten Extrem.
Unterschiede zur MetaTrader-Version
Die StockSharp-Version verfolgt Positionen saldiert. Das Verhalten ist identisch mit der ursprünglichen Single-Order-Logik
weil jeweils nur ein Trade offen sein kann und die Position abgeflacht wird, bevor neue Einträge bewertet werden.
MetaTrader ruft die laufenden Eröffnungs-, Höchst- und Tiefststände bis iOpen/iHigh/iLow Anrufe im täglichen Zeitrahmen ab. Der Hafen wird neu aufgebaut
die gleichen Informationen von Intraday-Kerzen, wodurch verbotene Indikatoraufrufe vermieden werden und sichergestellt wird, dass die Daten auch dann verfügbar bleiben, wenn
Der Brokerage-Feed stellt keine täglichen Balken bereit.
Der Auftragsabschluss erfolgt über ClosePosition(), anstatt die Ticket-IDs in einer Schleife zu durchlaufen. Das Endergebnis ist das gleiche:
Die offene Belichtung wird entfernt, sobald die konfigurierte Schließzeit erreicht ist.
Wenn der PriceStep des Wertpapiers nicht angegeben ist, behandelt die Konvertierung den RangePointsThreshold als absolute Preisdistanz.
Dieser Sicherheitsfallback hält das System auf Instrumenten betriebsbereit, die Preise ohne Schrittmetadaten melden.
Nutzungshinweise
Volume ist in OnStarted auf TradeVolume gesetzt, daher wirkt sich eine Änderung des Parameters sofort auf nachfolgende Bestellungen ohne aus
den Rest des Codes ändern.
Stellen Sie bei der Auswahl eines anderen CandleType sicher, dass dieser immer noch genügend Granularität bietet, um die Intraday-Eröffnungs-/Höchst-/Tiefstwerte zu verfolgen
genau. Fünf-Minuten-Kerzen funktionieren beispielsweise gut, stündliche Balken können jedoch die Erkennung von Tagesextremen verzögern.
Erhöhen Sie RangePointsThreshold, um Sitzungen mit geringer Volatilität herauszufiltern. Wenn Sie ihn verringern, kann die Strategie auch dann ausgelöst werden, wenn
Der frühe Bereich ist klein.
Da der Algorithmus alle Positionen am Ende des Tages schließt, ist keine Übernachtmarge erforderlich. Makler, die durchsetzen
Bei Sitzungsunterbrechungen werden auch die internen Range-Zähler automatisch zurückgesetzt, wenn der Handel wieder aufgenommen wird.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SecondEasiest: RSI reversal with EMA filter and ATR stops.
/// </summary>
public class SecondEasiestStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevRsi;
private decimal _entryPrice;
public SecondEasiestStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_emaLength = Param(nameof(EmaLength), 25)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = 0; _entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0; _entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
}
_prevRsi = rsiVal;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, AverageTrueRange
class second_easiest_strategy(Strategy):
def __init__(self):
super(second_easiest_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._ema_length = self.Param("EmaLength", 25) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._prev_rsi = 0.0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(second_easiest_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
ev = float(ema_val)
av = float(atr_val)
if self._prev_rsi == 0 or av <= 0:
self._prev_rsi = rv
return
close = float(candle.ClosePrice)
if self.Position > 0:
if close >= self._entry_price + av * 2.5 or close <= self._entry_price - av * 1.5 or rv > 70:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 2.5 or close >= self._entry_price + av * 1.5 or rv < 30:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_rsi = rv
return
if self.Position == 0:
if rv > 55 and self._prev_rsi <= 55 and close > ev:
self._entry_price = close
self.BuyMarket()
elif rv < 45 and self._prev_rsi >= 45 and close < ev:
self._entry_price = close
self.SellMarket()
self._prev_rsi = rv
def OnReseted(self):
super(second_easiest_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._entry_price = 0.0
def CreateClone(self):
return second_easiest_strategy()