ExpBuySellSide-Strategie
Diese Strategie konvertiert den MetaTrader Expert Advisor ExpBuySellSide in die StockSharp API. Sie kombiniert ein ATR-basiertes Stop-System mit einem vereinfachten Step Up/Down-Trendfilter.
Das ATR-Modul berechnet dynamische Stop-Levels um jede Kerze. Wenn der Preis über das obere Band bricht, gilt der Markt als bullisch; ein Bruch unter das untere Band zeigt eine bärische Phase an.
Das Step Up/Down-Modul vergleicht eine sehr schnelle SMA mit einer langsameren SMA und prüft, ob der Abstand zwischen ihnen zunimmt. Ein wachsender Abstand in Richtung des Crossovers bestätigt den Trend.
Ein Trade wird nur geöffnet, wenn beide Module in die gleiche Richtung zeigen. Bestehende Positionen können optional geschlossen werden, wenn ein entgegengesetztes Signal erscheint.
Details
- Einstiegskriterien:
- Long: Preis schließt über dem ATR-Oberband und die schnelle SMA entfernt sich von der langsamen SMA nach oben.
- Short: Preis schließt unter dem ATR-Unterband und die schnelle SMA entfernt sich von der langsamen SMA nach unten.
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Entgegengesetztes Signal erscheint und die Option Close Opposite ist aktiviert.
- Manueller Stop über Positionsschutz.
- Stops: Basierend auf
ATR * Multiplier-Bändern. - Standardwerte:
ATR Period= 5.ATR Multiplier= 2.5.Fast SMA= 2.Slow SMA= 30.Candle Type= 1-Stunden-Zeitrahmen.Close Opposite= true.
- Filter:
- Kategorie: Trendfolge
- Richtung: Beide
- Indikatoren: Mehrere
- Stops: Ja
- Komplexität: Moderat
- Zeitrahmen: Mittelfristig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Strategy that combines an ATR based stop with a simplified Step Up/Down trend filter.
/// A long position is opened when both modules signal an upward trend.
/// A short position is opened when both modules signal a downward trend.
/// </summary>
public class ExpBuySellSideStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<bool> _closeByOppositeSignal;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevDiff;
private decimal _prevUpper;
private decimal _prevLower;
private int _atrTrend;
/// <summary>
/// ATR calculation period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for stop calculation.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Fast SMA length used in trend filter.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow SMA length used in trend filter.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Close opposite position before opening new one.
/// </summary>
public bool CloseByOppositeSignal
{
get => _closeByOppositeSignal.Value;
set => _closeByOppositeSignal.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public ExpBuySellSideStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation length", "ATR")
.SetOptimize(3, 20, 1);
_atrMultiplier = Param(nameof(AtrMultiplier), 2.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR band multiplier", "ATR")
.SetOptimize(1m, 5m, 0.5m);
_fastPeriod = Param(nameof(FastPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Fast SMA", "Fast moving average length", "Step Up/Down")
.SetOptimize(1, 10, 1);
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow SMA", "Slow moving average length", "Step Up/Down")
.SetOptimize(10, 60, 5);
_closeByOppositeSignal = Param(nameof(CloseByOppositeSignal), true)
.SetDisplay("Close Opposite", "Close opposite position on signal", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevDiff = 0m;
_prevUpper = 0m;
_prevLower = 0m;
_atrTrend = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Start position protection once.
StartProtection(null, null);
var atr = new ATR { Length = AtrPeriod };
var fast = new SMA { Length = FastPeriod };
var slow = new SMA { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Step Up/Down filter using difference between fast and slow SMA.
var diff = fast - slow;
var stepSignal = 0;
if (fast > slow && diff > _prevDiff)
stepSignal = 1;
else if (fast < slow && diff < _prevDiff)
stepSignal = -1;
_prevDiff = diff;
// ATR based stop similar to SuperTrend.
var upper = candle.HighPrice - AtrMultiplier * atr;
var lower = candle.LowPrice + AtrMultiplier * atr;
var atrSignal = 0;
if (candle.ClosePrice > _prevUpper && _atrTrend <= 0)
{
_atrTrend = 1;
atrSignal = 1;
}
else if (candle.ClosePrice < _prevLower && _atrTrend >= 0)
{
_atrTrend = -1;
atrSignal = -1;
}
_prevUpper = upper;
_prevLower = lower;
var tradeSignal = 0;
if (atrSignal == 1 && stepSignal == 1)
tradeSignal = 1;
else if (atrSignal == -1 && stepSignal == -1)
tradeSignal = -1;
switch (tradeSignal)
{
case 1:
if (CloseByOppositeSignal && Position < 0)
BuyMarket(Volume + Math.Abs(Position));
else if (Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
break;
case -1:
if (CloseByOppositeSignal && Position > 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position >= 0)
SellMarket(Volume + Math.Abs(Position));
break;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class exp_buy_sell_side_strategy(Strategy):
def __init__(self):
super(exp_buy_sell_side_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 5)
self._atr_multiplier = self.Param("AtrMultiplier", 2.5)
self._fast_period = self.Param("FastPeriod", 5)
self._slow_period = self.Param("SlowPeriod", 30)
self._close_by_opposite_signal = self.Param("CloseByOppositeSignal", True)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_diff = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._atr_trend = 0
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def AtrMultiplier(self):
return self._atr_multiplier.Value
@AtrMultiplier.setter
def AtrMultiplier(self, value):
self._atr_multiplier.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def CloseByOppositeSignal(self):
return self._close_by_opposite_signal.Value
@CloseByOppositeSignal.setter
def CloseByOppositeSignal(self, value):
self._close_by_opposite_signal.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(exp_buy_sell_side_strategy, self).OnStarted2(time)
self._prev_diff = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._atr_trend = 0
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
fast = SimpleMovingAverage()
fast.Length = self.FastPeriod
slow = SimpleMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, atr, self.ProcessCandle).Start()
self.StartProtection(None, None)
def ProcessCandle(self, candle, fast_value, slow_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
fast = float(fast_value)
slow = float(slow_value)
atr = float(atr_value)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
mult = float(self.AtrMultiplier)
diff = fast - slow
step_signal = 0
if fast > slow and diff > self._prev_diff:
step_signal = 1
elif fast < slow and diff < self._prev_diff:
step_signal = -1
self._prev_diff = diff
upper = high - mult * atr
lower = low + mult * atr
atr_signal = 0
if close > self._prev_upper and self._atr_trend <= 0:
self._atr_trend = 1
atr_signal = 1
elif close < self._prev_lower and self._atr_trend >= 0:
self._atr_trend = -1
atr_signal = -1
self._prev_upper = upper
self._prev_lower = lower
trade_signal = 0
if atr_signal == 1 and step_signal == 1:
trade_signal = 1
elif atr_signal == -1 and step_signal == -1:
trade_signal = -1
pos = float(self.Position)
vol = float(self.Volume)
if trade_signal == 1:
if self.CloseByOppositeSignal and pos < 0:
self.BuyMarket(vol + abs(pos))
elif pos <= 0:
self.BuyMarket(vol + abs(pos))
elif trade_signal == -1:
if self.CloseByOppositeSignal and pos > 0:
self.SellMarket(vol + abs(pos))
elif pos >= 0:
self.SellMarket(vol + abs(pos))
def OnReseted(self):
super(exp_buy_sell_side_strategy, self).OnReseted()
self._prev_diff = 0.0
self._prev_upper = 0.0
self._prev_lower = 0.0
self._atr_trend = 0
def CreateClone(self):
return exp_buy_sell_side_strategy()