Auto Fibonacci and Gann Fan Retracements Combo
This strategy automatically detects new swing highs and lows. When an extremum is found it calculates Fibonacci and Gann retracement levels between the last swing points. The levels are written to the log and no trading logic is executed.
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>
/// Auto Fibonacci Gann Fan Retracements strategy.
/// Uses Highest/Lowest channel breakouts with EMA trend filter.
/// Buys when price breaks above highest channel with uptrend, sells on break below lowest with downtrend.
/// </summary>
public class AutoFiboGannFanRetracementsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _channelLength;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _cooldownBars;
private decimal _prevHighest;
private decimal _prevLowest;
private int _barIndex;
private int _lastTradeBar;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Channel lookback length.
/// </summary>
public int ChannelLength
{
get => _channelLength.Value;
set => _channelLength.Value = value;
}
/// <summary>
/// EMA trend filter period.
/// </summary>
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Cooldown bars between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public AutoFiboGannFanRetracementsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_channelLength = Param(nameof(ChannelLength), 20)
.SetGreaterThanZero()
.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators");
_emaLength = Param(nameof(EmaLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 350)
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHighest = 0;
_prevLowest = 0;
_barIndex = 0;
_lastTradeBar = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var highest = new Highest { Length = ChannelLength };
var lowest = new Lowest { Length = ChannelLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
var cooldownOk = _barIndex - _lastTradeBar > CooldownBars;
// Breakout above previous highest with uptrend
var breakUp = _prevHighest > 0 && candle.ClosePrice > _prevHighest && candle.ClosePrice > emaValue;
// Breakout below previous lowest with downtrend
var breakDown = _prevLowest > 0 && candle.ClosePrice < _prevLowest && candle.ClosePrice < emaValue;
if (breakUp && Position <= 0 && cooldownOk)
{
BuyMarket();
_lastTradeBar = _barIndex;
}
else if (breakDown && Position >= 0 && cooldownOk)
{
SellMarket();
_lastTradeBar = _barIndex;
}
_prevHighest = highestValue;
_prevLowest = lowestValue;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class auto_fibo_gann_fan_retracements_strategy(Strategy):
"""
Auto Fibonacci Gann Fan Retracements strategy.
Uses Highest/Lowest channel breakouts with EMA trend filter.
"""
def __init__(self):
super(auto_fibo_gann_fan_retracements_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(1)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._channel_length = self.Param("ChannelLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Channel Length", "Highest/Lowest lookback", "Indicators")
self._ema_length = self.Param("EmaLength", 50) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "EMA trend filter period", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 350) \
.SetDisplay("Cooldown Bars", "Bars between trades", "Trading")
self._prev_highest = 0.0
self._prev_lowest = 0.0
self._bar_index = 0
self._last_trade_bar = 0
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, v): self._candle_type.Value = v
@property
def ChannelLength(self): return self._channel_length.Value
@ChannelLength.setter
def ChannelLength(self, v): self._channel_length.Value = v
@property
def EmaLength(self): return self._ema_length.Value
@EmaLength.setter
def EmaLength(self, v): self._ema_length.Value = v
@property
def CooldownBars(self): return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, v): self._cooldown_bars.Value = v
def OnReseted(self):
super(auto_fibo_gann_fan_retracements_strategy, self).OnReseted()
self._prev_highest = 0.0
self._prev_lowest = 0.0
self._bar_index = 0
self._last_trade_bar = 0
def OnStarted2(self, time):
super(auto_fibo_gann_fan_retracements_strategy, self).OnStarted2(time)
highest = Highest()
highest.Length = self.ChannelLength
lowest = Lowest()
lowest.Length = self.ChannelLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, ema, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, highest_value, lowest_value, ema_value):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
cooldown_ok = self._bar_index - self._last_trade_bar > self.CooldownBars
close = float(candle.ClosePrice)
break_up = self._prev_highest > 0 and close > self._prev_highest and close > ema_value
break_down = self._prev_lowest > 0 and close < self._prev_lowest and close < ema_value
if break_up and self.Position <= 0 and cooldown_ok:
self.BuyMarket()
self._last_trade_bar = self._bar_index
elif break_down and self.Position >= 0 and cooldown_ok:
self.SellMarket()
self._last_trade_bar = self._bar_index
self._prev_highest = highest_value
self._prev_lowest = lowest_value
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return auto_fibo_gann_fan_retracements_strategy()