Strategie Donky MA TP SL
Diese Strategie handelt gleitende Durchschnitt-Kreuzungen mit zwei Take-Profit-Zielen und einem Stop-Loss. Sie geht Long, wenn der schnelle SMA den langsamen SMA nach oben kreuzt, und Short, wenn er ihn nach unten kreuzt. Die Hälfte der Position wird beim ersten Ziel geschlossen, der Rest beim zweiten Ziel oder dem Stop-Loss.
Details
- Einstiegskriterien:
- Long: Schneller SMA kreuzt über den langsamen SMA.
- Short: Schneller SMA kreuzt unter den langsamen SMA.
- Long/Short: Beide.
- Ausstiegskriterien: Zwei feste Take-Profit-Level oder ein fester Stop-Loss.
- Stops: Ja.
- Standardwerte:
FastLength= 10SlowLength= 30TakeProfit1Pct= 0.03mTakeProfit2Pct= 0.06mStopLossPct= 0.01mCandleType= TimeSpan.FromMinutes(1)
- Filter:
- Kategorie: Trend
- Richtung: Beide
- Indikatoren: SMA
- Stops: Ja
- Komplexität: Grundlegend
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Niedrig
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Donky MA TP SL strategy.
/// The fast SMA crossing above the slow SMA goes long and crossing below goes short, reversing an opposite position.
/// Half of the position closes at the first take-profit level and the remainder at the second level or at the stop-loss.
/// The levels are fractions of the entry price (0.03 means 3%).
/// </summary>
public class DonkyMaTpSlStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _takeProfit1Pct;
private readonly StrategyParam<decimal> _takeProfit2Pct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal _entryPrice;
private bool _firstTargetDone;
/// <summary>
/// Fast SMA length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow SMA length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// First take-profit distance as a fraction of the entry price.
/// </summary>
public decimal TakeProfit1Pct
{
get => _takeProfit1Pct.Value;
set => _takeProfit1Pct.Value = value;
}
/// <summary>
/// Second take-profit distance as a fraction of the entry price.
/// </summary>
public decimal TakeProfit2Pct
{
get => _takeProfit2Pct.Value;
set => _takeProfit2Pct.Value = value;
}
/// <summary>
/// Stop-loss distance as a fraction of the entry price.
/// </summary>
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DonkyMaTpSlStrategy()
{
_fastLength = Param(nameof(FastLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast SMA length", "Indicators");
_slowLength = Param(nameof(SlowLength), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow SMA length", "Indicators");
_takeProfit1Pct = Param(nameof(TakeProfit1Pct), 0.03m)
.SetNotNegative()
.SetDisplay("Take Profit 1", "First target as a fraction of the entry price", "Risk");
_takeProfit2Pct = Param(nameof(TakeProfit2Pct), 0.06m)
.SetNotNegative()
.SetDisplay("Take Profit 2", "Second target as a fraction of the entry price", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 0.01m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop distance as a fraction of the entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_prevFast = null;
_prevSlow = null;
_entryPrice = 0m;
_firstTargetDone = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fast = new SimpleMovingAverage { Length = FastLength };
var slow = new SimpleMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, 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)
{
if (candle.State != CandleStates.Finished)
return;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (ManagePosition(candle))
return;
if (prevFast is not decimal pf || prevSlow is not decimal ps)
return;
var crossUp = pf <= ps && fast > slow;
var crossDown = pf >= ps && fast < slow;
if (crossUp && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
_firstTargetDone = false;
}
else if (crossDown && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
_firstTargetDone = false;
}
}
// Returns true when the position was closed completely on this candle.
private bool ManagePosition(ICandleMessage candle)
{
if (Position == 0 || _entryPrice <= 0)
return false;
if (Position > 0)
{
var stop = _entryPrice * (1 - StopLossPct);
var target1 = _entryPrice * (1 + TakeProfit1Pct);
var target2 = _entryPrice * (1 + TakeProfit2Pct);
if (StopLossPct > 0 && candle.LowPrice <= stop)
{
SellMarket(Position);
return true;
}
if (TakeProfit2Pct > 0 && candle.HighPrice >= target2)
{
SellMarket(Position);
return true;
}
if (!_firstTargetDone && TakeProfit1Pct > 0 && candle.HighPrice >= target1)
{
_firstTargetDone = true;
SellMarket(Position / 2);
}
}
else
{
var stop = _entryPrice * (1 + StopLossPct);
var target1 = _entryPrice * (1 - TakeProfit1Pct);
var target2 = _entryPrice * (1 - TakeProfit2Pct);
var volume = Math.Abs(Position);
if (StopLossPct > 0 && candle.HighPrice >= stop)
{
BuyMarket(volume);
return true;
}
if (TakeProfit2Pct > 0 && candle.LowPrice <= target2)
{
BuyMarket(volume);
return true;
}
if (!_firstTargetDone && TakeProfit1Pct > 0 && candle.LowPrice <= target1)
{
_firstTargetDone = true;
BuyMarket(volume / 2);
}
}
return false;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class donky_ma_tp_sl_strategy(Strategy):
"""
Donky MA TP SL strategy.
The fast SMA crossing above the slow SMA goes long and crossing below goes short, reversing an opposite position.
Half of the position closes at the first take-profit level and the remainder at the second level or at the stop-loss.
The levels are fractions of the entry price (0.03 means 3%).
"""
def __init__(self):
super(donky_ma_tp_sl_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 10).SetGreaterThanZero().SetDisplay("Fast Length", "Fast SMA length", "Indicators")
self._slow_length = self.Param("SlowLength", 30).SetGreaterThanZero().SetDisplay("Slow Length", "Slow SMA length", "Indicators")
self._take_profit1_pct = self.Param("TakeProfit1Pct", 0.03).SetNotNegative().SetDisplay("Take Profit 1", "First target as a fraction of the entry price", "Risk")
self._take_profit2_pct = self.Param("TakeProfit2Pct", 0.06).SetNotNegative().SetDisplay("Take Profit 2", "Second target as a fraction of the entry price", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 0.01).SetNotNegative().SetDisplay("Stop Loss", "Stop distance as a fraction of the entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = None
self._prev_slow = None
self._entry_price = Decimal(0)
self._first_target_done = False
def OnReseted(self):
super(donky_ma_tp_sl_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(donky_ma_tp_sl_strategy, self).OnStarted2(time)
self._reset_state()
fast = SimpleMovingAverage()
fast.Length = self._fast_length.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._manage_position(candle):
return
if prev_fast is None or prev_slow is None:
return
cross_up = prev_fast <= prev_slow and fast > slow
cross_down = prev_fast >= prev_slow and fast < slow
if cross_up and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._entry_price = candle.ClosePrice
self._first_target_done = False
elif cross_down and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._entry_price = candle.ClosePrice
self._first_target_done = False
# Returns True when the position was closed completely on this candle.
def _manage_position(self, candle):
if self.Position == 0 or self._entry_price <= 0:
return False
tp1 = Decimal(self._take_profit1_pct.Value)
tp2 = Decimal(self._take_profit2_pct.Value)
sl = Decimal(self._stop_loss_pct.Value)
one = Decimal(1)
two = Decimal(2)
if self.Position > 0:
stop = self._entry_price * (one - sl)
target1 = self._entry_price * (one + tp1)
target2 = self._entry_price * (one + tp2)
if sl > 0 and candle.LowPrice <= stop:
self.SellMarket(self.Position)
return True
if tp2 > 0 and candle.HighPrice >= target2:
self.SellMarket(self.Position)
return True
if not self._first_target_done and tp1 > 0 and candle.HighPrice >= target1:
self._first_target_done = True
self.SellMarket(self.Position / two)
else:
stop = self._entry_price * (one + sl)
target1 = self._entry_price * (one - tp1)
target2 = self._entry_price * (one - tp2)
volume = abs(self.Position)
if sl > 0 and candle.HighPrice >= stop:
self.BuyMarket(volume)
return True
if tp2 > 0 and candle.LowPrice <= target2:
self.BuyMarket(volume)
return True
if not self._first_target_done and tp1 > 0 and candle.LowPrice <= target1:
self._first_target_done = True
self.BuyMarket(volume / two)
return False
def CreateClone(self):
return donky_ma_tp_sl_strategy()