Стратегия Trailing Stop Activation
Обзор
Стратегия с трейлинг-стопом управляет защитными стоп-заявками для уже открытых позиций. Она не генерирует входы, а служит для фиксации прибыли путем перемещения стопов.
Параметры
TrailingStop– расстояние в ценовых единицах, после прохождения которого активируется трейлинг-стоп.StopLoss– начальный размер стоп-лосса в ценовых единицах. Значение0отключает его.CandleType– тип используемых свечей для отслеживания цены.
Правила торговли
- При открытии позиции устанавливается стартовый стоп-лосс, если
StopLossбольше нуля. - Как только прибыль превышает
TrailingStop, стоп-уровень начинает следовать за ценой на указанном расстоянии. - При касании ценой стоп-уровня позиция закрывается.
- Стратегия работает как для длинных, так и для коротких позиций.
Примечания
Стратегия предназначена для совместной работы с другими стратегиями, которые обеспечивают входы в рынок. Она отвечает только за выход по трейлинг-стопу.
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>
/// Strategy with EMA direction entries and trailing stop management.
/// Enters on EMA direction change, exits via trailing stop.
/// </summary>
public class TrailingStopActivationStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevEma;
private decimal _prevPrevEma;
private int _count;
private decimal _entryPrice;
private decimal _stopPrice;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public decimal TrailingStop { get => _trailingStop.Value; set => _trailingStop.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TrailingStopActivationStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for entries", "Indicator");
_trailingStop = Param(nameof(TrailingStop), 500m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop", "Trailing stop distance", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0;
_prevPrevEma = 0;
_count = 0;
_entryPrice = 0;
_stopPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
SubscribeCandles(CandleType)
.Bind(ema, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
// Trailing stop check
if (Position > 0)
{
var trail = close - TrailingStop;
if (trail > _stopPrice)
_stopPrice = trail;
if (candle.LowPrice <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
else if (Position < 0)
{
var trail = close + TrailingStop;
if (_stopPrice == 0 || trail < _stopPrice)
_stopPrice = trail;
if (candle.HighPrice >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
_count++;
if (_count < 3)
{
_prevPrevEma = _prevEma;
_prevEma = emaValue;
return;
}
// Entry on EMA direction change
var turnUp = _prevEma < _prevPrevEma && emaValue > _prevEma;
var turnDown = _prevEma > _prevPrevEma && emaValue < _prevEma;
if (turnUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_entryPrice = close;
_stopPrice = close - TrailingStop;
}
else if (turnDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_entryPrice = close;
_stopPrice = close + TrailingStop;
}
_prevPrevEma = _prevEma;
_prevEma = emaValue;
}
}
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
from datatype_extensions import *
from indicator_extensions import *
class trailing_stop_activation_strategy(Strategy):
"""EMA direction change entries with trailing stop management."""
def __init__(self):
super(trailing_stop_activation_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14).SetGreaterThanZero().SetDisplay("EMA Period", "EMA period for entries", "Indicator")
self._trailing_stop = self.Param("TrailingStop", 500.0).SetGreaterThanZero().SetDisplay("Trailing Stop", "Trailing stop distance", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Type of candles", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(trailing_stop_activation_strategy, self).OnReseted()
self._prev_ema = 0
self._prev_prev_ema = 0
self._count = 0
self._entry_price = 0
self._stop_price = 0
def OnStarted2(self, time):
super(trailing_stop_activation_strategy, self).OnStarted2(time)
self._prev_ema = 0
self._prev_prev_ema = 0
self._count = 0
self._entry_price = 0
self._stop_price = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema_val)
trail = self._trailing_stop.Value
# Trailing stop check
if self.Position > 0:
new_trail = close - trail
if new_trail > self._stop_price:
self._stop_price = new_trail
if float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
self._entry_price = 0
self._stop_price = 0
elif self.Position < 0:
new_trail = close + trail
if self._stop_price == 0 or new_trail < self._stop_price:
self._stop_price = new_trail
if float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
self._entry_price = 0
self._stop_price = 0
self._count += 1
if self._count < 3:
self._prev_prev_ema = self._prev_ema
self._prev_ema = ema_val
return
# Entry on EMA direction change
turn_up = self._prev_ema < self._prev_prev_ema and ema_val > self._prev_ema
turn_down = self._prev_ema > self._prev_prev_ema and ema_val < self._prev_ema
if turn_up and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._stop_price = close - trail
elif turn_down and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._stop_price = close + trail
self._prev_prev_ema = self._prev_ema
self._prev_ema = ema_val
def CreateClone(self):
return trailing_stop_activation_strategy()