Gann Swing Breakout
Strategy based on Gann Swing Breakout technique
Testing indicates an average annual return of about 82%. It performs best in the stocks market.
Gann Swing Breakout tracks swing highs and lows from Gann analysis. A breakout beyond the latest swing starts a trade in that direction and it stays open until an opposing swing is breached.
The method is designed for traders who view past swing points as important support and resistance. By trading the break, it attempts to ride the next leg of a trend.
Details
- Entry Criteria: Signals based on MA, Gann.
- Long/Short: Both directions.
- Exit Criteria: Opposite signal.
- Stops: No.
- Default Values:
SwingLookback= 5MaPeriod= 20CandleType= TimeSpan.FromMinutes(15)
- Filters:
- Category: Breakout
- Direction: Both
- Indicators: MA, Gann
- Stops: No
- Complexity: Basic
- Timeframe: Intraday (15m)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Linq;
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.
/// A swing high is a candle whose high exceeds the highs of SwingLookback candles on each side, a swing low the reverse.
/// A close above the latest swing high while above the SMA opens a long position, a close below the latest swing low
/// while below the SMA a short one. A position stays open until the opposing swing is breached.
/// </summary>
public class GannSwingBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _swingLookback;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly List<(decimal High, decimal Low)> _window = [];
private decimal? _swingHigh;
private decimal? _swingLow;
/// <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), 5)
.SetGreaterThanZero()
.SetDisplay("Swing Lookback", "Lookback period for swing high/low", "Trading parameters")
.SetOptimize(20, 60, 10);
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for trend filter MA", "Indicators")
.SetOptimize(40, 80, 10);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_window.Clear();
_swingHigh = null;
_swingLow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_window.Clear();
_swingHigh = null;
_swingLow = null;
var ma = new SimpleMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
UpdateSwings(candle);
if (!maValue.IsFormed || _swingHigh is not decimal swingHigh || _swingLow is not decimal swingLow)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var ma = maValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (close > swingHigh && close > ma && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
}
else if (close < swingLow && close < ma && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
}
else if (Position > 0 && close < swingLow)
{
SellMarket(Position);
}
else if (Position < 0 && close > swingHigh)
{
BuyMarket(-Position);
}
}
private void UpdateSwings(ICandleMessage candle)
{
// A pivot is confirmed once SwingLookback candles have closed after it.
_window.Add((candle.HighPrice, candle.LowPrice));
var size = 2 * SwingLookback + 1;
if (_window.Count > size)
_window.RemoveAt(0);
if (_window.Count < size)
return;
var pivot = _window[SwingLookback];
var others = _window.Where((_, index) => index != SwingLookback).ToArray();
if (others.All(c => pivot.High > c.High))
_swingHigh = pivot.High;
if (others.All(c => pivot.Low < c.Low))
_swingLow = pivot.Low;
}
}
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, Decimal
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.
A swing high is a candle whose high exceeds the highs of SwingLookback candles on each side, a swing low the reverse.
A close above the latest swing high while above the SMA opens a long position, a close below the latest swing low
while below the SMA a short one. A position stays open until the opposing swing is breached.
"""
def __init__(self):
super(gann_swing_breakout_strategy, self).__init__()
self._swing_lookback = self.Param("SwingLookback", 5) \
.SetGreaterThanZero() \
.SetDisplay("Swing Lookback", "Lookback period for swing high/low", "Trading parameters")
self._ma_period = self.Param("MaPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Period for trend filter MA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._window = []
self._swing_high = None
self._swing_low = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(gann_swing_breakout_strategy, self).OnReseted()
self._window = []
self._swing_high = None
self._swing_low = None
def OnStarted2(self, time):
super(gann_swing_breakout_strategy, self).OnStarted2(time)
self._window = []
self._swing_high = None
self._swing_low = None
ma = SimpleMovingAverage()
ma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(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
self._update_swings(candle)
if not ma_value.IsFormed or self._swing_high is None or self._swing_low is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ma = ma_value.GetValue[Decimal](None)
close = candle.ClosePrice
position = self.Position
if close > self._swing_high and close > ma and position <= 0:
self.BuyMarket(self.Volume + Math.Abs(position))
elif close < self._swing_low and close < ma and position >= 0:
self.SellMarket(self.Volume + Math.Abs(position))
elif position > 0 and close < self._swing_low:
self.SellMarket(position)
elif position < 0 and close > self._swing_high:
self.BuyMarket(-position)
def _update_swings(self, candle):
# A pivot is confirmed once SwingLookback candles have closed after it.
lookback = self._swing_lookback.Value
self._window.append((candle.HighPrice, candle.LowPrice))
size = 2 * lookback + 1
if len(self._window) > size:
self._window.pop(0)
if len(self._window) < size:
return
pivot_high, pivot_low = self._window[lookback]
others = self._window[:lookback] + self._window[lookback + 1:]
if all(pivot_high > high for high, _ in others):
self._swing_high = pivot_high
if all(pivot_low < low for _, low in others):
self._swing_low = pivot_low
def CreateClone(self):
return gann_swing_breakout_strategy()