Пробой по Ганну (Gann Swing Breakout)
Стратегия основана на технике Gann Swing Breakout.
Тестирование показывает среднегодичную доходность около 82%. Стратегию лучше запускать на фондовом рынке.
Gann Swing Breakout отслеживает экстремумы свингов по анализу Ганна. Прорыв за последний свинг открывает сделку в этом направлении и удерживает её, пока не будет пробит противоположный свинг.
Метод рассчитан на трейдеров, которые считают прошлые свинг-точки важными уровнями поддержки и сопротивления. Торгуя на пробой, стратегия пытается поймать следующую волну тренда.
Подробности
- Критерии входа: сигналы на основе MA и Ганна.
- Длинные/короткие: оба направления.
- Критерии выхода: противоположный сигнал.
- Стопы: нет.
- Параметры по умолчанию:
SwingLookback= 5MaPeriod= 20CandleType= TimeSpan.FromMinutes(15)
- Фильтры:
- Категория: Пробой
- Направление: Оба
- Индикаторы: MA, Gann
- Стопы: Нет
- Сложность: Базовая
- Таймфрейм: Внутридневной (15м)
- Сезонность: Нет
- Нейронные сети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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 based on Gann Swing Breakout technique.
/// Uses Donchian channel breakouts with SMA trend filter.
/// Enters long when price breaks above channel high and is above SMA.
/// Enters short when price breaks below channel low and is below SMA.
/// </summary>
public class GannSwingBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _swingLookback;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevChannelHigh;
private decimal _prevChannelLow;
private bool _hasPrevValues;
private int _candlesSinceLastTrade;
/// <summary>
/// Number of bars to identify swing points.
/// </summary>
public int SwingLookback
{
get => _swingLookback.Value;
set => _swingLookback.Value = value;
}
/// <summary>
/// Period for moving average calculation.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize the Gann Swing Breakout strategy.
/// </summary>
public GannSwingBreakoutStrategy()
{
_swingLookback = Param(nameof(SwingLookback), 40)
.SetDisplay("Swing Lookback", "Lookback period for swing high/low", "Trading parameters")
.SetOptimize(20, 60, 10);
_maPeriod = Param(nameof(MaPeriod), 60)
.SetDisplay("MA Period", "Period for trend filter MA", "Indicators")
.SetOptimize(40, 80, 10);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevChannelHigh = default;
_prevChannelLow = default;
_hasPrevValues = default;
_candlesSinceLastTrade = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var donchian = new DonchianChannels { Length = SwingLookback };
var ma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(donchian, ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue donchianValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (maValue is not { IsEmpty: false })
return;
var ma = maValue.GetValue<decimal>();
if (ma == 0)
return;
// Extract Donchian channel values
var dcValue = (IDonchianChannelsValue)donchianValue;
if (dcValue.UpperBand is not decimal channelHigh ||
dcValue.LowerBand is not decimal channelLow)
return;
if (channelHigh == 0 || channelLow == 0)
return;
if (!_hasPrevValues)
{
_hasPrevValues = true;
_prevChannelHigh = channelHigh;
_prevChannelLow = channelLow;
return;
}
_candlesSinceLastTrade++;
// Breakout above previous channel high + above MA = buy
if (_candlesSinceLastTrade >= 10 && candle.ClosePrice > _prevChannelHigh && candle.ClosePrice > ma && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_candlesSinceLastTrade = 0;
}
// Breakout below previous channel low + below MA = sell
else if (_candlesSinceLastTrade >= 10 && candle.ClosePrice < _prevChannelLow && candle.ClosePrice < ma && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_candlesSinceLastTrade = 0;
}
_prevChannelHigh = channelHigh;
_prevChannelLow = channelLow;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class gann_swing_breakout_strategy(Strategy):
"""
Gann Swing Breakout: Donchian channel breakout with SMA trend filter.
Buys when price breaks above previous channel high and is above SMA.
Sells when price breaks below previous channel low and is below SMA.
"""
def __init__(self):
super(gann_swing_breakout_strategy, self).__init__()
self._swing_lookback = self.Param("SwingLookback", 40) \
.SetDisplay("Swing Lookback", "Lookback period for swing high/low", "Trading parameters")
self._ma_period = self.Param("MaPeriod", 60) \
.SetDisplay("MA Period", "Period for trend filter MA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_channel_high = 0.0
self._prev_channel_low = 0.0
self._has_prev_values = False
self._candles_since_last_trade = 0
self._highs = []
self._lows = []
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(gann_swing_breakout_strategy, self).OnReseted()
self._prev_channel_high = 0.0
self._prev_channel_low = 0.0
self._has_prev_values = False
self._candles_since_last_trade = 0
self._highs = []
self._lows = []
def OnStarted2(self, time):
super(gann_swing_breakout_strategy, self).OnStarted2(time)
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
ma = float(ma_value)
if ma == 0:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
lookback = self._swing_lookback.Value
self._highs.append(high)
self._lows.append(low)
while len(self._highs) > lookback:
self._highs.pop(0)
while len(self._lows) > lookback:
self._lows.pop(0)
if len(self._highs) < lookback:
return
channel_high = max(self._highs)
channel_low = min(self._lows)
if channel_high == 0 or channel_low == 0:
return
if not self._has_prev_values:
self._has_prev_values = True
self._prev_channel_high = channel_high
self._prev_channel_low = channel_low
return
self._candles_since_last_trade += 1
if self._candles_since_last_trade >= 10 and close > self._prev_channel_high and close > ma and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._candles_since_last_trade = 0
elif self._candles_since_last_trade >= 10 and close < self._prev_channel_low and close < ma and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._candles_since_last_trade = 0
self._prev_channel_high = channel_high
self._prev_channel_low = channel_low
def CreateClone(self):
return gann_swing_breakout_strategy()