Averaging Down-Strategie
Diese Strategie eröffnet eine Position, wenn der Preis außerhalb eines ATR-basierten Bandes um den EMA bewegt. Wenn sich der Markt gegen die Position entwickelt, fügt die Strategie mit schrittweise skalierten prozentualen Abweichungen (DCA) hinzu. Gewinn wird genommen, wenn der Preis zum gemittelten Einstieg plus einem festen Prozentsatz zurückkehrt.
Parameter
- Candle Type – zu verarbeitende Kerzentypen.
- EMA Length – Periode für den EMA-Trendfilter.
- ATR Length – Periode für ATR.
- ATR Mult – Multiplikator für ATR-Bänder.
- TP % – Take-Profit-Prozentsatz vom durchschnittlichen Einstieg.
- Base Deviation % – anfängliche Abweichung für das erste DCA-Niveau.
- Step Scale – Multiplikator für die Abweichung bei jedem neuen DCA-Niveau.
- DCA Size Multiplier – Volumen-Multiplikator für jede DCA-Order.
- Max DCA Levels – maximale Anzahl von Mittelungseinstiegen.
- Initial Volume – Volumen der ersten Order.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Averaging Down Strategy.
/// Uses EMA + ATR bands to detect entry zones, then averages down
/// if price moves against position. Takes profit at target %.
/// </summary>
public class AveragingDownStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _tpPercent;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ema;
private AverageTrueRange _atr;
private decimal _entryPrice;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
public decimal TpPercent
{
get => _tpPercent.Value;
set => _tpPercent.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public AveragingDownStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_emaLength = Param(nameof(EmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetDisplay("ATR Multiplier", "ATR band multiplier", "Indicators");
_tpPercent = Param(nameof(TpPercent), 2m)
.SetDisplay("TP %", "Take profit percent", "Trading");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_atr = null;
_entryPrice = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaLength };
_atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, _atr, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_atr.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
return;
}
var price = candle.ClosePrice;
var upperBand = emaVal + atrVal * AtrMultiplier;
var lowerBand = emaVal - atrVal * AtrMultiplier;
// Entry: price breaks above upper band -> buy
if (price > upperBand && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_entryPrice = price;
_cooldownRemaining = CooldownBars;
}
// Entry: price breaks below lower band -> sell
else if (price < lowerBand && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_entryPrice = price;
_cooldownRemaining = CooldownBars;
}
// Take profit on long
else if (Position > 0 && _entryPrice > 0 && price >= _entryPrice * (1 + TpPercent / 100m))
{
SellMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
}
// Take profit on short
else if (Position < 0 && _entryPrice > 0 && price <= _entryPrice * (1 - TpPercent / 100m))
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
}
// Exit long at EMA (stop)
else if (Position > 0 && price < emaVal)
{
SellMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
}
// Exit short at EMA (stop)
else if (Position < 0 && price > emaVal)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0;
_cooldownRemaining = CooldownBars;
}
}
}
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.Indicators import ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class averaging_down_strategy(Strategy):
"""Averaging Down Strategy."""
def __init__(self):
super(averaging_down_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "EMA period", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "ATR band multiplier", "Indicators")
self._tp_percent = self.Param("TpPercent", 2.0) \
.SetDisplay("TP %", "Take profit percent", "Trading")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ema = None
self._atr = None
self._entry_price = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(averaging_down_strategy, self).OnReseted()
self._ema = None
self._atr = None
self._entry_price = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(averaging_down_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
self._atr = AverageTrueRange()
self._atr.Length = int(self._atr_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._atr, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._atr.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
return
price = float(candle.ClosePrice)
ema_v = float(ema_val)
atr_v = float(atr_val)
multiplier = float(self._atr_multiplier.Value)
upper_band = ema_v + atr_v * multiplier
lower_band = ema_v - atr_v * multiplier
tp_pct = float(self._tp_percent.Value)
cooldown = int(self._cooldown_bars.Value)
if price > upper_band and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._entry_price = price
self._cooldown_remaining = cooldown
elif price < lower_band and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._entry_price = price
self._cooldown_remaining = cooldown
elif self.Position > 0 and self._entry_price > 0 and price >= self._entry_price * (1 + tp_pct / 100.0):
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
elif self.Position < 0 and self._entry_price > 0 and price <= self._entry_price * (1 - tp_pct / 100.0):
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
elif self.Position > 0 and price < ema_v:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
elif self.Position < 0 and price > ema_v:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._cooldown_remaining = cooldown
def CreateClone(self):
return averaging_down_strategy()