Главная
/
Примеры стратегий
Открыть на GitHub
Стратегия Fractal ZigZag
Эта стратегия представляет собой прямую конвертацию советника MetaTrader 4 Fractal ZigZag Expert.mq4 . Она заново строит
последовательность фракталов Билла Уильямса и трактует последнюю подтверждённую экстремальную точку как активную фазу рынка.
Если последним сформировался фрактал-минимум, стратегия открывает длинную позицию; если подтверждён фрактал-максимум — короткую.
Все исходные параметры — глубина фрактала, тейк-профит, стартовый стоп и трейлинг-стоп — сохранены, но заявочная логика
адаптирована под высокоуровневый API StockSharp.
Лучше всего стратегия работает на часовых свечах (H1), как и оригинальный советник. При необходимости параметр CandleType
позволяет переключиться на любой другой таймфрейм из доступных. Все расстояния задаются в ценовых пунктах (шагов цены
инструмента), полностью повторяя использование константы Point в MetaTrader.
Правила торговли
Определение сигнала
Каждый завершённый бар добавляется в скользящее окно размером 2 * Level + 1.
Фрактал-максимум подтверждается, если центральная свеча имеет наибольший максимум в окне; для фрактала-минимума требуется
наименьший минимум.
Последний подтверждённый фрактал определяет направление: минимум задаёт внутренний тренд 2 (покупки), максимум — 1
(продажи).
Входы
Когда внутренний тренд равен 2 и позиций нет, отправляется рыночная покупка объёмом Lots.
При тренде 1 и отсутствии позиции выполняется рыночная продажа.
После закрытия сделки стратегия вновь войдёт в том же направлении, если тренд не сменился.
Выходы и управление риском
Каждой сделке назначаются исходный стоп-лосс и фиксированный тейк-профит (в пунктах). Значение 0 отключает соответствующую
защиту.
Дополнительный трейлинг-стоп (также в пунктах) активируется после прохода ценой заданного расстояния и поддерживает одинаковый
отступ от цены закрытия, не пересекающий стартовый стоп.
Контроль уровней реализован через анализ максимумов и минимумов свечей, что позволяет приблизить поведение к оригиналу на MQL4.
Параметры по умолчанию
Параметр
Значение
Описание
Level
2
Число свечей по обе стороны, необходимое для подтверждения фрактала.
TakeProfitPoints
25
Расстояние до тейк-профита в ценовых пунктах.
InitialStopPoints
20
Расстояние до исходного стоп-лосса в ценовых пунктах.
TrailingStopPoints
10
Дистанция трейлинг-стопа (значение 0 отключает его).
Lots
1
Объём рыночных заявок.
CandleType
H1
Таймфрейм свечей, используемый в расчётах.
Примечания
В начале работы вызывается StartProtection(), чтобы StockSharp мог задействовать аварийное закрытие позиции при необходимости.
Комментарии в коде ведутся только на английском языке, а описания в README подготовлены на требуемых языках.
Реализация обходится без индикаторных буферов и хранит лишь минимальное окно данных, необходимое для проверки фрактала.
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>
/// Fractal ZigZag: Confirms Bill Williams fractals then trades
/// in the direction of the last confirmed extremum.
/// Bullish after low fractal, bearish after high fractal.
/// </summary>
public class FractalZigZagStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _level;
private readonly StrategyParam<int> _atrLength;
private readonly List<(decimal high, decimal low, DateTimeOffset time)> _window = new();
private int _trend; // 1=bearish (last was high), 2=bullish (last was low)
private int _prevTrend;
private decimal _entryPrice;
public FractalZigZagStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_level = Param(nameof(Level), 2)
.SetDisplay("Fractal Depth", "Candles on each side to confirm fractal.", "Signals");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int Level
{
get => _level.Value;
set => _level.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_window.Clear();
_trend = 0;
_prevTrend = 0;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
// Update fractal window
var depth = Math.Max(1, Level);
var windowSize = depth * 2 + 1;
_window.Add((candle.HighPrice, candle.LowPrice, candle.OpenTime));
while (_window.Count > windowSize)
_window.RemoveAt(0);
// Evaluate fractals
if (_window.Count >= windowSize)
{
var centerIndex = _window.Count - 1 - depth;
var center = _window[centerIndex];
var isHigh = true;
var isLow = true;
for (var i = 0; i < _window.Count; i++)
{
if (i == centerIndex)
continue;
if (_window[i].high >= center.high)
isHigh = false;
if (_window[i].low <= center.low)
isLow = false;
if (!isHigh && !isLow)
break;
}
if (isHigh)
_trend = 1; // bearish: last fractal was a high
if (isLow)
_trend = 2; // bullish: last fractal was a low
}
if (atrVal <= 0 || _trend == 0)
{
_prevTrend = _trend;
return;
}
var close = candle.ClosePrice;
// Exit management
if (Position > 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m || _trend == 1)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m || _trend == 2)
{
BuyMarket();
_entryPrice = 0;
}
}
// Entry on trend change
if (Position == 0 && _prevTrend != 0 && _trend != _prevTrend)
{
if (_trend == 2)
{
_entryPrice = close;
BuyMarket();
}
else if (_trend == 1)
{
_entryPrice = close;
SellMarket();
}
}
_prevTrend = _trend;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import AverageTrueRange
class fractal_zig_zag_strategy(Strategy):
def __init__(self):
super(fractal_zig_zag_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._level = self.Param("Level", 2) \
.SetDisplay("Fractal Depth", "Candles on each side to confirm fractal", "Signals")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period for stops", "Indicators")
self._window = []
self._trend = 0 # 1=bearish (last was high), 2=bullish (last was low)
self._prev_trend = 0
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def Level(self):
return self._level.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(fractal_zig_zag_strategy, self).OnStarted2(time)
self._window = []
self._trend = 0
self._prev_trend = 0
self._entry_price = 0.0
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
av = float(atr_val)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
depth = max(1, self.Level)
window_size = depth * 2 + 1
self._window.append((high, low))
while len(self._window) > window_size:
self._window.pop(0)
# Evaluate fractals
if len(self._window) >= window_size:
center_index = len(self._window) - 1 - depth
center = self._window[center_index]
is_high = True
is_low = True
for i in range(len(self._window)):
if i == center_index:
continue
if self._window[i][0] >= center[0]:
is_high = False
if self._window[i][1] <= center[1]:
is_low = False
if not is_high and not is_low:
break
if is_high:
self._trend = 1
if is_low:
self._trend = 2
if av <= 0 or self._trend == 0:
self._prev_trend = self._trend
return
# Exit management
if self.Position > 0:
if close <= self._entry_price - av * 2.0 or close >= self._entry_price + av * 3.0 or self._trend == 1:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close >= self._entry_price + av * 2.0 or close <= self._entry_price - av * 3.0 or self._trend == 2:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_trend = self._trend
return
# Entry on trend change
if self.Position == 0 and self._prev_trend != 0 and self._trend != self._prev_trend:
if self._trend == 2:
self._entry_price = close
self.BuyMarket()
elif self._trend == 1:
self._entry_price = close
self.SellMarket()
self._prev_trend = self._trend
def OnReseted(self):
super(fractal_zig_zag_strategy, self).OnReseted()
self._window = []
self._trend = 0
self._prev_trend = 0
self._entry_price = 0.0
def CreateClone(self):
return fractal_zig_zag_strategy()