StockSharp-Port des MetaTrader 4-Expertenberaters SimpleTrade.mq4 (auch bekannt als „neroTrade“).
Konzipiert für den Handel mit Einzelsymbolen innerhalb des über den Parameter CandleType konfigurierten Zeitrahmens.
Behält immer höchstens eine offene Position bei und ändert die Richtung bei der Eröffnung jedes neuen Balkens.
Handelslogik
Jedes Mal, wenn eine neue Kerze aktiv wird, vergleicht die Strategie den Eröffnungspreis der Kerze mit dem Eröffnungspreis der Kerze, die LookbackBars Perioden älter ist.
Wenn die neue Eröffnung deutlich über dem historischen Referenzwert liegt, werden alle bestehenden Positionen geschlossen und eine neue Long-Market-Order mit TradeVolume Lots übermittelt.
Andernfalls (offen ist gleich oder niedriger) schließt die Strategie alle bestehenden Positionen und eröffnet eine Short-Marktposition derselben Größe.
Der Parameter StopLossPoints spiegelt die Einstellung stop des ursprünglichen EA wider. Wenn sowohl PriceStep als auch StopLossPoints des Wertpapiers verfügbar sind, wandelt die Strategie den Wert in einen absoluten Abstand um und leitet ihn an StartProtection weiter, sodass StockSharp die schützenden Stop-Loss-Orders automatisch aufrechterhalten kann.
Kerzenöffnungen werden mithilfe des High-Level-Kerzenabonnements API verfolgt. Fertige Kerzen füllen die Verlaufsliste, während die aktive Kerze die Entscheidung einmal pro Balken auslöst.
Parameter
Parameter
Beschreibung
Standard
TradeVolume
Basisauftragsgröße, ausgedrückt in Losen. Muss positiv sein.
1
StopLossPoints
Schutzanschlagabstand in Instrumentenpunkten. Auf 0 setzen, um den automatischen Stop-Loss zu deaktivieren.
120
LookbackBars
Anzahl der für den offenen Preisvergleich verwendeten Balken. Ein Wert von 3 reproduziert Open[0] gegenüber Open[3] aus dem Originalcode.
3
CandleType
Zeitrahmen (als DataType), ab dem Kerzen angefordert werden. Steuert, wann neue Signale erscheinen.
1 hour timeframe
Implementierungshinweise
Verwendet den High-Level-Workflow SubscribeCandles(...).Bind(...), sodass die Strategie leichtgewichtig bleibt und sowohl auf historische als auch auf Live-Kerzen reagiert.
StartProtection wird einmal während OnStarted aufgerufen. Stellen Sie sicher, dass die verbundene Sicherheit PriceStep bietet; Andernfalls kann die Stop-Loss-Distanz nicht in absolute Preise umgerechnet werden.
Da alle Geschäfte mit Marktaufträgen zu Beginn jedes Balkens eingegeben werden, wird die Slippage-Behandlung an den Handelsplatz delegiert und es gibt keinen zusätzlichen slippage-Parameter.
Der historische offene Puffer behält nur ein kleines rollierendes Fenster (LookbackBars + 5-Werte), um unnötigen Speicherverbrauch zu vermeiden.
Es wird kein Python-Port bereitgestellt. Das Verzeichnis CS/ enthält die einzige Implementierung.
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>
/// StockSharp port of the MetaTrader "SimpleTrade" expert advisor.
/// Compares the opening price of the current bar with the bar from several periods ago and flips the position accordingly.
/// </summary>
public class SimpleTradeFlipStrategy : Strategy
{
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<int> _lookbackBars;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _openHistory = new();
private int _cooldown;
/// <summary>
/// Initializes a new instance of the <see cref="SimpleTradeFlipStrategy"/> class.
/// </summary>
public SimpleTradeFlipStrategy()
{
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Order size in lots", "Trading");
_stopLossPoints = Param(nameof(StopLossPoints), 120m)
.SetNotNegative()
.SetDisplay("Stop-Loss Points", "Protective stop distance expressed in instrument points", "Risk");
_lookbackBars = Param(nameof(LookbackBars), 10)
.SetGreaterThanZero()
.SetDisplay("Lookback Bars", "Number of bars used for the open price comparison", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General");
}
/// <summary>
/// Order size submitted with each entry.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Stop-loss distance in instrument points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = Math.Max(0m, value);
}
/// <summary>
/// Number of historical bars used for the open price comparison.
/// </summary>
public int LookbackBars
{
get => _lookbackBars.Value;
set => _lookbackBars.Value = Math.Max(1, value);
}
/// <summary>
/// Candle type that defines the working timeframe.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_openHistory.Clear();
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var step = Security?.PriceStep ?? 0m;
Unit stopLossUnit = null;
if (StopLossPoints > 0m && step > 0m)
stopLossUnit = new Unit(StopLossPoints * step, UnitTypes.Absolute);
StartProtection(null, stopLossUnit);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Store the open price for future comparisons.
_openHistory.Add(candle.OpenPrice);
var maxHistory = Math.Max(LookbackBars + 5, 5);
if (_openHistory.Count > maxHistory)
_openHistory.RemoveRange(0, _openHistory.Count - maxHistory);
var lookback = LookbackBars;
if (_openHistory.Count <= lookback)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var volume = TradeVolume;
if (volume <= 0m)
return;
var currentOpen = candle.OpenPrice;
var referenceOpen = _openHistory[^(lookback + 1)];
// Only trade on clear directional difference
var diff = currentOpen - referenceOpen;
if (Math.Abs(diff) < currentOpen * 0.001m)
return;
if (diff > 0 && Position <= 0)
{
if (Position < 0m)
BuyMarket(Math.Abs(Position));
BuyMarket(volume);
_cooldown = 5;
}
else if (diff < 0 && Position >= 0)
{
if (Position > 0m)
SellMarket(Position);
SellMarket(volume);
_cooldown = 5;
}
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
class simple_trade_flip_strategy(Strategy):
def __init__(self):
super(simple_trade_flip_strategy, self).__init__()
self._trade_volume = self.Param("TradeVolume", 1.0) \
.SetDisplay("Trade Volume", "Order size in lots", "Trading")
self._stop_loss_points = self.Param("StopLossPoints", 120.0) \
.SetDisplay("Stop-Loss Points", "Protective stop distance in instrument points", "Risk")
self._lookback_bars = self.Param("LookbackBars", 10) \
.SetDisplay("Lookback Bars", "Number of bars used for open price comparison", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Primary timeframe used for signal calculations", "General")
self._open_history = []
self._cooldown = 0
@property
def TradeVolume(self):
return self._trade_volume.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def LookbackBars(self):
return self._lookback_bars.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(simple_trade_flip_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
ps = self.Security.PriceStep if self.Security is not None else None
step = float(ps) if ps is not None and float(ps) > 0 else 0.0
sl_pts = float(self.StopLossPoints)
sl = Unit(sl_pts * step, UnitTypes.Absolute) if sl_pts > 0 and step > 0 else None
self.StartProtection(None, sl)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
self._open_history.append(float(candle.OpenPrice))
max_history = max(self.LookbackBars + 5, 5)
if len(self._open_history) > max_history:
self._open_history = self._open_history[-max_history:]
lookback = int(self.LookbackBars)
if len(self._open_history) <= lookback:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown > 0:
self._cooldown -= 1
return
volume = float(self.TradeVolume)
if volume <= 0:
return
current_open = float(candle.OpenPrice)
reference_open = self._open_history[-(lookback + 1)]
diff = current_open - reference_open
if abs(diff) < current_open * 0.001:
return
if diff > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(volume)
self._cooldown = 5
elif diff < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket(self.Position)
self.SellMarket(volume)
self._cooldown = 5
def OnReseted(self):
super(simple_trade_flip_strategy, self).OnReseted()
self._open_history = []
self._cooldown = 0
def CreateClone(self):
return simple_trade_flip_strategy()