Стратегия Disturbed
Эта хеджирующая стратегия одновременно открывает покупку и продажу по рынку и управляет ими на основе текущего спреда. Когда цена смещается на один спред против какой‑либо позиции, эта позиция закрывается. Оставшаяся позиция затем закрывается при прибыли или убытке, равных настраиваемому коэффициенту спреда.
Подробности
- Условия входа:
- При запуске отправляются рыночные ордера на покупку и продажу.
- Длинные/короткие: Оба одновременно.
- Условия выхода:
- Закрытие стороны, потерявшей один спред.
- Закрытие оставшейся стороны при прибыли или убытке
gainMultiplier * spread.
- Стопы: Реализованы через уровни на базе спреда.
- Фильтры: Нет.
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>
/// Hedging-style strategy using EMA crossover with ATR-based mean reversion.
/// </summary>
public class DisturbedStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevEma;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DisturbedStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0; _hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var atr = new StandardDeviation { Length = AtrPeriod };
SubscribeCandles(CandleType).Bind(ema, atr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal atr)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevEma = ema; _hasPrev = true; return; }
var close = candle.ClosePrice;
// Price crosses above EMA + ATR => buy
if (close > ema + atr && _prevEma > 0 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Price crosses below EMA - ATR => sell
else if (close < ema - atr && _prevEma > 0 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
// Exit long when price returns to EMA
else if (Position > 0 && close <= ema)
{
SellMarket();
}
// Exit short when price returns to EMA
else if (Position < 0 && close >= ema)
{
BuyMarket();
}
_prevEma = ema;
}
}
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, StandardDeviation
from StockSharp.Algo.Strategies import Strategy
class disturbed_strategy(Strategy):
def __init__(self):
super(disturbed_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 12) \
.SetDisplay("EMA Period", "EMA period", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_ema = 0.0
self._has_prev = False
@property
def ema_period(self):
return self._ema_period.Value
@property
def atr_period(self):
return self._atr_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(disturbed_strategy, self).OnReseted()
self._prev_ema = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(disturbed_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
atr = StandardDeviation()
atr.Length = self.atr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, ema, atr):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_ema = ema
self._has_prev = True
return
close = candle.ClosePrice
# Price crosses above EMA + ATR => buy
if close > ema + atr and self._prev_ema > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Price crosses below EMA - ATR => sell
elif close < ema - atr and self._prev_ema > 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
# Exit long when price returns to EMA
elif self.Position > 0 and close <= ema:
self.SellMarket()
# Exit short when price returns to EMA
elif self.Position < 0 and close >= ema:
self.BuyMarket()
self._prev_ema = ema
def CreateClone(self):
return disturbed_strategy()