Стратегия Pull Back переносит логику оригинального советника MetaTrader "PULL BACK" в инфраструктуру StockSharp. Алгоритм ищет откаты к быстрой взвешенной скользящей средней на старшем таймфрейме, проверяет силу импульса за несколько свечей и торгует в сторону тренда по индикатору MACD на длинном горизонте. После входа в позицию включаются правила управления риском: стоп-лосс, тейк-профит, перевод в безубыток и трейлинг-стоп.
Данные и индикаторы
Торговый таймфрейм: настраиваемый тип свечей (CandleType, по умолчанию 15 минут).
Подтверждающий таймфрейм: отдельная подписка (HigherCandleType, по умолчанию 1 час) для:
Быстрой и медленной WMA.
Индикатора Momentum с расчётом модуля отклонения от нейтрального уровня 100.
Обнаружения отката, когда свеча касается быстрой WMA.
Таймфрейм MACD: отдельная подписка (MacdCandleType, по умолчанию 30 дней) для оценки направления линии сигнала MACD.
Используемые индикаторы:
Взвешенная скользящая средняя (WMA) на рабочем и старшем таймфреймах.
Momentum с заданным периодом на старшем таймфрейме.
Moving Average Convergence Divergence (MACD) на длинном таймфрейме.
Логика входа
Длинные позиции
На старшем таймфрейме быстрая WMA выше медленной.
Последняя завершённая свеча открылась выше быстрой WMA и касалась её минимумом.
Как минимум одно из трёх последних значений |Momentum-100| превышает MomentumBuyThreshold.
Основная линия MACD выше сигнальной на выбранном таймфрейме MACD.
На торговом таймфрейме быстрая WMA выше медленной.
При выполнении всех условий отправляется рыночная заявка на покупку, а цена входа сохраняется для контроля рисков.
Короткие позиции
На старшем таймфрейме быстрая WMA ниже медленной.
Последняя свеча открылась ниже быстрой WMA и касалась её максимумом.
Одно из последних трёх значений |Momentum-100| превышает MomentumSellThreshold.
Основная линия MACD ниже сигнальной.
На торговом таймфрейме быстрая WMA ниже медленной.
При выполнении условий стратегия продаёт по рынку.
Управление позицией
Стоп-лосс: расстояние StopLossTicks от цены входа (пересчёт в абсолютное значение через Security.PriceStep).
Тейк-профит: расстояние TakeProfitTicks от цены входа.
Безубыток: при продвижении цены на BreakEvenTriggerTicks стоп переносится на уровень входа плюс BreakEvenOffsetTicks в сторону сделки (если UseBreakEven = true).
Трейлинг-стоп: при UseTrailingStop = true стоп следует за ценой с отступом TrailingStopTicks.
Проверка выхода: выполняется на каждой завершённой свече торгового таймфрейма; при достижении стопа или цели позиция закрывается рыночной заявкой.
Параметры
Параметр
Описание
FastMaLength
Период быстрой WMA на торговом таймфрейме (по умолчанию 6).
SlowMaLength
Период медленной WMA на торговом таймфрейме (по умолчанию 85).
BounceSlowLength
Период медленной WMA на подтверждающем таймфрейме (по умолчанию 200).
MomentumLength
Период Momentum на старшем таймфрейме (по умолчанию 14).
MomentumBuyThreshold
Минимальное
Momentum-100
для входа в лонг (по умолчанию 0.3).
MomentumSellThreshold
Минимальное
Momentum-100
для входа в шорт (по умолчанию 0.3).
StopLossTicks
Стоп-лосс в тиках (по умолчанию 200).
TakeProfitTicks
Тейк-профит в тиках (по умолчанию 500).
UseTrailingStop
Включение трейлинг-стопа (по умолчанию true).
TrailingStopTicks
Отступ трейлинг-стопа в тиках (по умолчанию 400).
UseBreakEven
Включение перевода в безубыток (по умолчанию true).
BreakEvenTriggerTicks
Триггер перевода в безубыток в тиках (по умолчанию 300).
BreakEvenOffsetTicks
Смещение стопа после перевода в безубыток в тиках (по умолчанию 300).
MacdFastLength
Быстрый период EMA для MACD (по умолчанию 12).
MacdSlowLength
Медленный период EMA для MACD (по умолчанию 26).
MacdSignalLength
Период сигнальной EMA MACD (по умолчанию 9).
CandleType
Тип свечей торгового таймфрейма.
HigherCandleType
Тип свечей подтверждающего таймфрейма.
MacdCandleType
Тип свечей для расчёта MACD.
Примечания
Для корректного пересчёта тиковых расстояний необходимо, чтобы Security.PriceStep был задан поставщиком данных.
Стратегия удерживает только одну суммарную позицию; сигналы в противоположную сторону игнорируются до закрытия текущей.
Обработка выполняется только по завершённым свечам, чтобы исключить реакцию на неполные данные.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class PullBackStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public PullBackStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 5).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 15).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
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;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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 pull_back_strategy(Strategy):
def __init__(self):
super(pull_back_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 15) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(pull_back_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(pull_back_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).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 _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return pull_back_strategy()