MM Fibonacci Strategy
This strategy calculates Murrey Math Fibonacci levels and trades breakouts. It buys when price breaks above the 100% level in an upward context and sells when price drops below the 0% level in a downward context. Positions are closed when price crosses the 50% level against the trade.
Details
- Entry Criteria:
- Long: Price closes above the 100% level while the most recent extreme was a high.
- Short: Price closes below the 0% level while the most recent extreme was a low.
- Exit Criteria:
- Long: Price falls below the 50% level.
- Short: Price rises above the 50% level.
- Indicators: Highest, Lowest.
- Long/Short: Both.
- Stops: No.
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>
/// Murrey Math Fibonacci breakout strategy.
/// </summary>
public class MmFibonacciStrategy : Strategy
{
private readonly StrategyParam<int> _frame;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private Highest _highest;
private Lowest _lowest;
private int _sinceHigh;
private int _sinceLow;
private int _prevFibDir;
private int _barsFromSignal;
public int Frame { get => _frame.Value; set => _frame.Value = value; }
public decimal Multiplier { get => _multiplier.Value; set => _multiplier.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MmFibonacciStrategy()
{
_frame = Param(nameof(Frame), 64).SetGreaterThanZero();
_multiplier = Param(nameof(Multiplier), 1.5m).SetGreaterThanZero();
_signalCooldownBars = Param(nameof(SignalCooldownBars), 12).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_highest = null;
_lowest = null;
_sinceHigh = 0;
_sinceLow = 0;
_prevFibDir = 0;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_sinceHigh = 0;
_sinceLow = 0;
_prevFibDir = 0;
_barsFromSignal = SignalCooldownBars;
var length = (int)Math.Round(Frame * Multiplier);
_highest = new Highest { Length = length };
_lowest = new Lowest { Length = length };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_highest, _lowest, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal nHigh, decimal nLow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_highest.IsFormed || !_lowest.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var range = nHigh - nLow;
if (range <= 0m)
return;
decimal fractal;
if (nHigh <= 250000m && nHigh > 25000m)
fractal = 100000m;
else if (nHigh <= 25000m && nHigh > 2500m)
fractal = 10000m;
else if (nHigh <= 2500m && nHigh > 250m)
fractal = 1000m;
else if (nHigh <= 250m && nHigh > 25m)
fractal = 100m;
else if (nHigh <= 25m && nHigh > 6.25m)
fractal = 12.5m;
else if (nHigh <= 6.25m && nHigh > 3.125m)
fractal = 6.25m;
else if (nHigh <= 3.125m && nHigh > 1.5625m)
fractal = 3.125m;
else if (nHigh <= 1.5625m && nHigh > 0.390625m)
fractal = 1.5625m;
else
fractal = 0.1953125m;
var sum = (decimal)Math.Floor(Math.Log((double)(fractal / range), 2));
var octave = fractal * (decimal)Math.Pow(0.5, (double)sum);
var minimum = Math.Floor(nLow / octave) * octave;
var maximum = (minimum + octave) > nHigh ? minimum + octave : minimum + 2m * octave;
decimal t1 = 0m, t2 = 0m, t3 = 0m, t4 = 0m, t5 = 0m;
var diff = maximum - minimum;
if (nLow >= (3m * diff / 16m + minimum) && nHigh <= (9m * diff / 16m + minimum))
t2 = minimum + diff / 2m;
if (nLow >= (minimum - diff / 8m) && nHigh <= (5m * diff / 8m + minimum) && t2 == 0m)
t1 = minimum + diff / 2m;
if (nLow >= (minimum + 7m * diff / 16m) && nHigh <= (13m * diff / 16m + minimum))
t4 = minimum + 3m * diff / 4m;
if (nLow >= (minimum + 3m * diff / 8m) && nHigh <= (9m * diff / 8m + minimum) && t4 == 0m)
t5 = maximum;
if (nLow >= (minimum + diff / 8m) && nHigh <= (7m * diff / 8m + minimum) && t1 == 0m && t2 == 0m && t4 == 0m && t5 == 0m)
t3 = minimum + 3m * diff / 4m;
var t6 = (t1 + t2 + t3 + t4 + t5) == 0m ? maximum : 0m;
var top = t1 + t2 + t3 + t4 + t5 + t6;
decimal b1 = 0m, b2 = 0m, b3 = 0m, b4 = 0m, b5 = 0m;
if (t1 > 0m) b1 = minimum;
if (t2 > 0m) b2 = minimum + diff / 4m;
if (t3 > 0m) b3 = minimum + diff / 4m;
if (t4 > 0m) b4 = minimum + diff / 2m;
if (t5 > 0m) b5 = minimum + diff / 2m;
var b6 = (top > 0m && (b1 + b2 + b3 + b4 + b5) == 0m) ? minimum : 0m;
var bottom = b1 + b2 + b3 + b4 + b5 + b6;
var fibRange = top - bottom;
if (fibRange <= 0m)
return;
_sinceHigh = candle.HighPrice >= top ? 0 : _sinceHigh + 1;
_sinceLow = candle.LowPrice <= bottom ? 0 : _sinceLow + 1;
var fibDirUp = _sinceHigh > _sinceLow;
var fibDirDn = _sinceHigh < _sinceLow;
var fibDir = fibDirUp ? 1 : fibDirDn ? -1 : 0;
_barsFromSignal++;
if (fibDir != 0 && fibDir != _prevFibDir && _barsFromSignal >= SignalCooldownBars)
{
if (fibDir > 0 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_barsFromSignal = 0;
}
else if (fibDir < 0 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_barsFromSignal = 0;
}
}
if (fibDir != 0)
_prevFibDir = fibDir;
}
}
import clr
import math
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class mm_fibonacci_strategy(Strategy):
def __init__(self):
super(mm_fibonacci_strategy, self).__init__()
self._frame = self.Param("Frame", 64) \
.SetGreaterThanZero()
self._multiplier = self.Param("Multiplier", 1.5) \
.SetGreaterThanZero()
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 12) \
.SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._since_high = 0
self._since_low = 0
self._prev_fib_dir = 0
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(mm_fibonacci_strategy, self).OnReseted()
self._since_high = 0
self._since_low = 0
self._prev_fib_dir = 0
self._bars_from_signal = 0
def OnStarted2(self, time):
super(mm_fibonacci_strategy, self).OnStarted2(time)
self._since_high = 0
self._since_low = 0
self._prev_fib_dir = 0
self._bars_from_signal = self._signal_cooldown_bars.Value
length = int(round(self._frame.Value * float(self._multiplier.Value)))
self._highest = Highest()
self._highest.Length = length
self._lowest = Lowest()
self._lowest.Length = length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._highest, self._lowest, self.OnProcess).Start()
def OnProcess(self, candle, n_high, n_low):
if candle.State != CandleStates.Finished:
return
if not self._highest.IsFormed or not self._lowest.IsFormed:
return
nh = float(n_high)
nl = float(n_low)
rng = nh - nl
if rng <= 0.0:
return
if nh <= 250000.0 and nh > 25000.0:
fractal = 100000.0
elif nh <= 25000.0 and nh > 2500.0:
fractal = 10000.0
elif nh <= 2500.0 and nh > 250.0:
fractal = 1000.0
elif nh <= 250.0 and nh > 25.0:
fractal = 100.0
elif nh <= 25.0 and nh > 6.25:
fractal = 12.5
elif nh <= 6.25 and nh > 3.125:
fractal = 6.25
elif nh <= 3.125 and nh > 1.5625:
fractal = 3.125
elif nh <= 1.5625 and nh > 0.390625:
fractal = 1.5625
else:
fractal = 0.1953125
s = math.floor(math.log(fractal / rng, 2))
octave = fractal * (0.5 ** s)
minimum = math.floor(nl / octave) * octave
maximum = minimum + octave if (minimum + octave) > nh else minimum + 2.0 * octave
t1 = 0.0
t2 = 0.0
t3 = 0.0
t4 = 0.0
t5 = 0.0
diff = maximum - minimum
if nl >= (3.0 * diff / 16.0 + minimum) and nh <= (9.0 * diff / 16.0 + minimum):
t2 = minimum + diff / 2.0
if nl >= (minimum - diff / 8.0) and nh <= (5.0 * diff / 8.0 + minimum) and t2 == 0.0:
t1 = minimum + diff / 2.0
if nl >= (minimum + 7.0 * diff / 16.0) and nh <= (13.0 * diff / 16.0 + minimum):
t4 = minimum + 3.0 * diff / 4.0
if nl >= (minimum + 3.0 * diff / 8.0) and nh <= (9.0 * diff / 8.0 + minimum) and t4 == 0.0:
t5 = maximum
if nl >= (minimum + diff / 8.0) and nh <= (7.0 * diff / 8.0 + minimum) and t1 == 0.0 and t2 == 0.0 and t4 == 0.0 and t5 == 0.0:
t3 = minimum + 3.0 * diff / 4.0
t6 = maximum if (t1 + t2 + t3 + t4 + t5) == 0.0 else 0.0
top = t1 + t2 + t3 + t4 + t5 + t6
b1 = 0.0
b2 = 0.0
b3 = 0.0
b4 = 0.0
b5 = 0.0
if t1 > 0.0:
b1 = minimum
if t2 > 0.0:
b2 = minimum + diff / 4.0
if t3 > 0.0:
b3 = minimum + diff / 4.0
if t4 > 0.0:
b4 = minimum + diff / 2.0
if t5 > 0.0:
b5 = minimum + diff / 2.0
b6 = minimum if (top > 0.0 and (b1 + b2 + b3 + b4 + b5) == 0.0) else 0.0
bottom = b1 + b2 + b3 + b4 + b5 + b6
fib_range = top - bottom
if fib_range <= 0.0:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
self._since_high = 0 if high >= top else self._since_high + 1
self._since_low = 0 if low <= bottom else self._since_low + 1
fib_dir_up = self._since_high > self._since_low
fib_dir_dn = self._since_high < self._since_low
fib_dir = 1 if fib_dir_up else (-1 if fib_dir_dn else 0)
self._bars_from_signal += 1
if fib_dir != 0 and fib_dir != self._prev_fib_dir and self._bars_from_signal >= self._signal_cooldown_bars.Value:
if fib_dir > 0 and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif fib_dir < 0 and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
if fib_dir != 0:
self._prev_fib_dir = fib_dir
def CreateClone(self):
return mm_fibonacci_strategy()