Стратегия стабилизации ордеров — это конверсия эксперта MetaTrader hjueiisyx8lp2o379e_www_forex-instruments_info.mq4. Изначальный робот выставляет пару стоповых ордеров вокруг текущей цены и ждёт пробоя. После открытия позиции система отслеживает размеры тел последних свечей, чтобы определить, застыл ли рынок, и закрывает сделку при потере импульса или достижении заданной прибыли.
Версия на C# воспроизводит те же правила с использованием высокоуровневого API StockSharp и работает только по закрытым свечам. Это делает поведение стратегии воспроизводимым как на тестах, так и в реальной торговле.
Правила торговли
При отсутствии позиций и активных заявок стратегия размещает buy stop выше рынка и sell stop ниже рынка на расстоянии OrderDistancePoints пунктов (MetaTrader points).
При исполнении стоп-заявки:
Открывается позиция объёмом OrderVolume лотов.
Противоположный стоп остаётся в стакане, чтобы поймать разворот в другую сторону.
Пока позиция открыта, анализируются тела двух последних завершённых свечей:
Если тело последней свечи меньше StabilizationPoints, а плавающая прибыль превышает ProfitThreshold, позиция закрывается, а противоположный стоп отменяется.
Если две подряд свечи укладываются в предел StabilizationPoints, позиция закрывается вне зависимости от текущего результата.
Если прибыль достигает AbsoluteFixation, сделка закрывается немедленно.
Стоп-заявки снимаются после истечения ExpirationMinutes, если параметр больше нуля.
Параметры
Имя
Описание
Значение по умолчанию
OrderVolume
Объём сделки в лотах.
0.1
OrderDistancePoints
Расстояние до стоповых ордеров в пунктах MetaTrader.
20
ProfitThreshold
Минимальная прибыль (в валюте счёта) для выхода по стабилизации.
-2
AbsoluteFixation
Уровень прибыли (валюта счёта), при котором позиция закрывается безусловно.
30
StabilizationPoints
Максимальный размер тела свечи, считающийся признаком флэта.
25
ExpirationMinutes
Время жизни стоп-заявок в минутах, 0 — без ограничения.
20
CandleType
Тип свечи для анализа стабилизации (по умолчанию 5-минутные).
TimeFrame(5m)
Особенности конверсии
В оригинале расчёты велись по тикам; в порте используется завершённая свеча, что упрощает тестирование.
Пункты MetaTrader сопоставлены с PriceStep. Если шаг цены не задан, используется значение 1.
Прибыль пересчитывается через PriceStep и StepPrice, чтобы получить оценку в валюте счёта.
Все комментарии и описания параметров переведены на английский язык в соответствии с требованиями репозитория.
Использование
Добавьте стратегию в свой проект StockSharp и назначьте инструмент с портфелем.
Настройте параметры, уделив особое внимание таймфрейму свечей и расстоянию до стопов.
Запустите стратегию — она автоматически выставит стоповые ордера и будет сопровождать позицию согласно описанным правилам.
Идеи для развития
Подбирать таймфрейм под волатильность инструмента, чтобы избежать ложных выходов.
Добавить фильтры волатильности (ATR, полосы Боллинджера) для работы только в активные периоды.
Реализовать трейлинг-стоп или частичную фиксацию прибыли при приближении к целевому уровню.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Order stabilization strategy - trades breakouts after periods of low volatility.
/// When candle body is small (stabilization), waits for a directional breakout.
/// Goes long on bullish breakout candle after stabilization, short on bearish.
/// </summary>
public class OrderStabilizationStrategy : Strategy
{
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _stabilizationFactor;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevBody;
private decimal _prevAtr;
private bool _hasPrev;
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal StabilizationFactor { get => _stabilizationFactor.Value; set => _stabilizationFactor.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public OrderStabilizationStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR period for volatility", "Indicators");
_stabilizationFactor = Param(nameof(StabilizationFactor), 0.5m)
.SetDisplay("Stabilization Factor", "Body must be less than ATR * factor for stabilization", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevBody = 0m;
_prevAtr = 0m;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var threshold = atrValue * StabilizationFactor;
if (!_hasPrev)
{
_prevBody = body;
_prevAtr = atrValue;
_hasPrev = true;
return;
}
var prevThreshold = _prevAtr * StabilizationFactor;
var wasStabilized = _prevBody < prevThreshold;
// After stabilization, trade breakout candle
if (wasStabilized && body > threshold)
{
var bullish = candle.ClosePrice > candle.OpenPrice;
if (bullish && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (!bullish && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
}
_prevBody = body;
_prevAtr = atrValue;
}
}
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 AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class order_stabilization_strategy(Strategy):
"""Trades breakouts after periods of low volatility (stabilization).
When previous candle body is small relative to ATR, waits for directional breakout.
Goes long on bullish breakout candle, short on bearish."""
def __init__(self):
super(order_stabilization_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR period for volatility", "Indicators")
self._stabilization_factor = self.Param("StabilizationFactor", 0.5) \
.SetDisplay("Stabilization Factor", "Body must be less than ATR * factor for stabilization", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_body = 0.0
self._prev_atr = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@property
def StabilizationFactor(self):
return self._stabilization_factor.Value
def OnReseted(self):
super(order_stabilization_strategy, self).OnReseted()
self._prev_body = 0.0
self._prev_atr = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(order_stabilization_strategy, self).OnStarted2(time)
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr_value)
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
body = abs(close - open_price)
factor = float(self.StabilizationFactor)
threshold = atr_val * factor
if not self._has_prev:
self._prev_body = body
self._prev_atr = atr_val
self._has_prev = True
return
prev_threshold = self._prev_atr * factor
was_stabilized = self._prev_body < prev_threshold
# After stabilization, trade breakout candle
if was_stabilized and body > threshold:
bullish = close > open_price
if bullish and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif not bullish and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_body = body
self._prev_atr = atr_val
def CreateClone(self):
return order_stabilization_strategy()