Стратегия RoNz Auto SL TS TP
Стратегия открывает позиции по пересечению EMA и автоматически управляет уровнями стоп-лосса и тейк-профита.
После входа она устанавливает начальные уровни, затем при желании фиксирует прибыль и включает трейлинг-стоп.
Подробности
- Условия входа:
- Long:
EMA10 < EMA20 && EMA10 > EMA100 - Short:
EMA10 > EMA20 && EMA10 < EMA100
- Long:
- Long/Short: Оба
- Условия выхода: Стоп-лосс, тейк-профит, фиксация прибыли или трейлинг-стоп
- Стопы: Да
- Значения по умолчанию:
TakeProfit= 500StopLoss= 250LockProfitAfter= 100ProfitLock= 60TrailingStop= 50TrailingStep= 10
- Фильтры:
- Категория: Управление рисками
- Направление: Оба
- Индикаторы: EMA
- Стопы: SL/TP/Trailing
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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 automatically manages stop-loss and take-profit levels.
/// Opens positions using EMA crossover and protects them with
/// profit lock and trailing stop.
/// </summary>
public class RoNzAutoSlTsTpStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _lockAfter;
private readonly StrategyParam<decimal> _profitLock;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<decimal> _trailingStep;
private readonly StrategyParam<int> _cooldownBars;
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Take profit distance in price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss distance in price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Profit threshold to start locking gains.
/// </summary>
public decimal LockProfitAfter
{
get => _lockAfter.Value;
set => _lockAfter.Value = value;
}
/// <summary>
/// Amount of profit to lock once threshold reached.
/// </summary>
public decimal ProfitLock
{
get => _profitLock.Value;
set => _profitLock.Value = value;
}
/// <summary>
/// Trailing stop distance.
/// </summary>
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
/// <summary>
/// Step to move trailing stop.
/// </summary>
public decimal TrailingStep
{
get => _trailingStep.Value;
set => _trailingStep.Value = value;
}
/// <summary>
/// Minimum number of bars between entries.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
private decimal _trailAnchor;
private decimal _prevEma10;
private decimal _prevEma20;
private bool _profitLocked;
private bool _isInitialized;
private int _barsSinceExit;
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public RoNzAutoSlTsTpStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for calculations", "General");
_takeProfit = Param(nameof(TakeProfit), 500m)
.SetDisplay("Take Profit", "Take profit in points", "Risk");
_stopLoss = Param(nameof(StopLoss), 250m)
.SetDisplay("Stop Loss", "Stop loss in points", "Risk");
_lockAfter = Param(nameof(LockProfitAfter), 100m)
.SetDisplay("Lock Profit After", "Profit threshold for locking", "Risk");
_profitLock = Param(nameof(ProfitLock), 60m)
.SetDisplay("Profit Lock", "Profit to lock after threshold", "Risk");
_trailingStop = Param(nameof(TrailingStop), 50m)
.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk");
_trailingStep = Param(nameof(TrailingStep), 10m)
.SetDisplay("Trailing Step", "Step for trailing stop", "Risk");
_cooldownBars = Param(nameof(CooldownBars), 6)
.SetDisplay("Cooldown Bars", "Minimum number of bars between entries", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetProtection();
_prevEma10 = 0m;
_prevEma20 = 0m;
_isInitialized = false;
_barsSinceExit = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema10 = new ExponentialMovingAverage { Length = 10 };
var ema20 = new ExponentialMovingAverage { Length = 20 };
var ema100 = new ExponentialMovingAverage { Length = 100 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema10, ema20, ema100, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema10, decimal ema20, decimal ema100)
{
if (candle.State != CandleStates.Finished)
return;
if (!_isInitialized)
{
_prevEma10 = ema10;
_prevEma20 = ema20;
_isInitialized = true;
return;
}
_barsSinceExit++;
var bullishCross = _prevEma10 <= _prevEma20 && ema10 > ema20 && ema10 > ema100 && ema20 > ema100;
var bearishCross = _prevEma10 >= _prevEma20 && ema10 < ema20 && ema10 < ema100 && ema20 < ema100;
if (Position == 0)
{
if (_barsSinceExit >= CooldownBars && bullishCross)
{
BuyMarket();
SetInitialProtection(candle.ClosePrice);
}
else if (_barsSinceExit >= CooldownBars && bearishCross)
{
SellMarket();
SetInitialProtection(candle.ClosePrice);
}
}
else
{
ManageProtection(candle.ClosePrice);
}
_prevEma10 = ema10;
_prevEma20 = ema20;
}
private void SetInitialProtection(decimal price)
{
_entryPrice = price;
_profitLocked = false;
_trailAnchor = price;
if (Position > 0)
{
_stopPrice = StopLoss > 0 ? price - StopLoss : 0m;
_takePrice = TakeProfit > 0 ? price + TakeProfit : 0m;
}
else if (Position < 0)
{
_stopPrice = StopLoss > 0 ? price + StopLoss : 0m;
_takePrice = TakeProfit > 0 ? price - TakeProfit : 0m;
}
}
private void ManageProtection(decimal price)
{
if (Position > 0)
{
if ((_takePrice > 0 && price >= _takePrice) || (_stopPrice > 0 && price <= _stopPrice))
{
SellMarket();
ResetProtection();
return;
}
var profit = price - _entryPrice;
if (!_profitLocked && LockProfitAfter > 0 && ProfitLock > 0 && profit >= LockProfitAfter)
{
_stopPrice = _entryPrice + ProfitLock;
_profitLocked = true;
}
if (TrailingStop > 0 && (LockProfitAfter == 0 || profit >= LockProfitAfter))
{
if (price - _trailAnchor >= TrailingStep)
{
var newStop = price - TrailingStop;
if (newStop > _stopPrice)
_stopPrice = newStop;
_trailAnchor = price;
}
}
}
else if (Position < 0)
{
if ((_takePrice > 0 && price <= _takePrice) || (_stopPrice > 0 && price >= _stopPrice))
{
BuyMarket();
ResetProtection();
return;
}
var profit = _entryPrice - price;
if (!_profitLocked && LockProfitAfter > 0 && ProfitLock > 0 && profit >= LockProfitAfter)
{
_stopPrice = _entryPrice - ProfitLock;
_profitLocked = true;
}
if (TrailingStop > 0 && (LockProfitAfter == 0 || profit >= LockProfitAfter))
{
if (_trailAnchor - price >= TrailingStep)
{
var newStop = price + TrailingStop;
if (_stopPrice == 0m || newStop < _stopPrice)
_stopPrice = newStop;
_trailAnchor = price;
}
}
}
}
private void ResetProtection()
{
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
_trailAnchor = 0m;
_profitLocked = false;
_barsSinceExit = 0;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ro_nz_auto_sl_ts_tp_strategy(Strategy):
def __init__(self):
super(ro_nz_auto_sl_ts_tp_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Timeframe for calculations", "General")
self._take_profit_param = self.Param("TakeProfit", 500.0) \
.SetDisplay("Take Profit", "Take profit in points", "Risk")
self._stop_loss_param = self.Param("StopLoss", 250.0) \
.SetDisplay("Stop Loss", "Stop loss in points", "Risk")
self._lock_after = self.Param("LockProfitAfter", 100.0) \
.SetDisplay("Lock Profit After", "Profit threshold for locking", "Risk")
self._profit_lock = self.Param("ProfitLock", 60.0) \
.SetDisplay("Profit Lock", "Profit to lock after threshold", "Risk")
self._trailing_stop = self.Param("TrailingStop", 50.0) \
.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk")
self._trailing_step = self.Param("TrailingStep", 10.0) \
.SetDisplay("Trailing Step", "Step for trailing stop", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 6) \
.SetDisplay("Cooldown Bars", "Minimum number of bars between entries", "Risk")
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._trail_anchor = 0.0
self._prev_ema10 = 0.0
self._prev_ema20 = 0.0
self._profit_locked = False
self._is_initialized = False
self._bars_since_exit = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def take_profit(self):
return self._take_profit_param.Value
@property
def stop_loss(self):
return self._stop_loss_param.Value
@property
def lock_profit_after(self):
return self._lock_after.Value
@property
def profit_lock(self):
return self._profit_lock.Value
@property
def trailing_stop(self):
return self._trailing_stop.Value
@property
def trailing_step(self):
return self._trailing_step.Value
@property
def cooldown_bars(self):
return self._cooldown_bars.Value
def OnReseted(self):
super(ro_nz_auto_sl_ts_tp_strategy, self).OnReseted()
self._reset_protection()
self._prev_ema10 = 0.0
self._prev_ema20 = 0.0
self._is_initialized = False
self._bars_since_exit = self.cooldown_bars
def _reset_protection(self):
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
self._trail_anchor = 0.0
self._profit_locked = False
self._bars_since_exit = 0
def OnStarted2(self, time):
super(ro_nz_auto_sl_ts_tp_strategy, self).OnStarted2(time)
ema10 = ExponentialMovingAverage()
ema10.Length = 10
ema20 = ExponentialMovingAverage()
ema20.Length = 20
ema100 = ExponentialMovingAverage()
ema100.Length = 100
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema10, ema20, ema100, self.process_candle).Start()
def process_candle(self, candle, ema10, ema20, ema100):
if candle.State != CandleStates.Finished:
return
e10 = float(ema10)
e20 = float(ema20)
e100 = float(ema100)
if not self._is_initialized:
self._prev_ema10 = e10
self._prev_ema20 = e20
self._is_initialized = True
return
self._bars_since_exit += 1
bullish_cross = self._prev_ema10 <= self._prev_ema20 and e10 > e20 and e10 > e100 and e20 > e100
bearish_cross = self._prev_ema10 >= self._prev_ema20 and e10 < e20 and e10 < e100 and e20 < e100
price = float(candle.ClosePrice)
if self.Position == 0:
if self._bars_since_exit >= self.cooldown_bars and bullish_cross:
self.BuyMarket()
self._set_initial_protection(price)
elif self._bars_since_exit >= self.cooldown_bars and bearish_cross:
self.SellMarket()
self._set_initial_protection(price)
else:
self._manage_protection(price)
self._prev_ema10 = e10
self._prev_ema20 = e20
def _set_initial_protection(self, price):
self._entry_price = price
self._profit_locked = False
self._trail_anchor = price
tp = float(self.take_profit)
sl = float(self.stop_loss)
if self.Position > 0:
self._stop_price = price - sl if sl > 0 else 0.0
self._take_price = price + tp if tp > 0 else 0.0
elif self.Position < 0:
self._stop_price = price + sl if sl > 0 else 0.0
self._take_price = price - tp if tp > 0 else 0.0
def _manage_protection(self, price):
lock_after = float(self.lock_profit_after)
p_lock = float(self.profit_lock)
ts = float(self.trailing_stop)
t_step = float(self.trailing_step)
if self.Position > 0:
if (self._take_price > 0 and price >= self._take_price) or (self._stop_price > 0 and price <= self._stop_price):
self.SellMarket()
self._reset_protection()
return
profit = price - self._entry_price
if not self._profit_locked and lock_after > 0 and p_lock > 0 and profit >= lock_after:
self._stop_price = self._entry_price + p_lock
self._profit_locked = True
if ts > 0 and (lock_after == 0 or profit >= lock_after):
if price - self._trail_anchor >= t_step:
new_stop = price - ts
if new_stop > self._stop_price:
self._stop_price = new_stop
self._trail_anchor = price
elif self.Position < 0:
if (self._take_price > 0 and price <= self._take_price) or (self._stop_price > 0 and price >= self._stop_price):
self.BuyMarket()
self._reset_protection()
return
profit = self._entry_price - price
if not self._profit_locked and lock_after > 0 and p_lock > 0 and profit >= lock_after:
self._stop_price = self._entry_price - p_lock
self._profit_locked = True
if ts > 0 and (lock_after == 0 or profit >= lock_after):
if self._trail_anchor - price >= t_step:
new_stop = price + ts
if self._stop_price == 0.0 or new_stop < self._stop_price:
self._stop_price = new_stop
self._trail_anchor = price
def CreateClone(self):
return ro_nz_auto_sl_ts_tp_strategy()