Bullish Reversal Strategy
Strategy that searches for classic bullish reversal candlestick formations. When any of these patterns appears below a 50-period simple moving average the strategy opens a long position. An optional trailing stop protects open profits.
Patterns
- Abandoned Baby – two consecutive bearish candles followed by a bullish candle that closes above the first candle's open while the second candle gaps lower.
- Morning Doji Star – a bearish candle, a doji or small-bodied candle, then a bullish candle closing back into the first candle's body.
- Three Inside Up – a bearish candle, a smaller bullish candle within its range, then a bullish candle closing above the first candle's open.
- Three Outside Up – a bearish candle followed by a larger bullish candle engulfing it and a third bullish candle confirming the reversal.
- Three White Soldiers – three consecutive bullish candles with rising closes.
Details
- Entry Criteria: any listed pattern and the last candle opened below the moving average
- Long/Short: Long
- Exit Criteria: optional trailing stop
- Stops: Trailing
- Default Values:
MaPeriod= 50TrailingStop= 50UseTrailingStop= true
- Filters:
- Category: Pattern
- Direction: Long
- Indicators: SMA
- Stops: Trailing
- Complexity: Basic
- Timeframe: Intraday
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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 looking for bullish/bearish reversal candlestick patterns with MA filter.
/// </summary>
public class BullishReversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private decimal _prevOpen1, _prevClose1, _prevLow1;
private decimal _prevOpen2, _prevClose2, _prevLow2;
private decimal _prevOpen3, _prevClose3, _prevLow3;
private int _candleCount;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public BullishReversalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_maPeriod = Param(nameof(MaPeriod), 50)
.SetDisplay("MA Period", "EMA length", "Parameters")
.SetGreaterThanZero();
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevOpen1 = 0; _prevClose1 = 0; _prevLow1 = 0;
_prevOpen2 = 0; _prevClose2 = 0; _prevLow2 = 0;
_prevOpen3 = 0; _prevClose3 = 0; _prevLow3 = 0;
_candleCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = MaPeriod };
SubscribeCandles(CandleType).Bind(ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ma)
{
if (candle.State != CandleStates.Finished) return;
_candleCount++;
if (_candleCount < 4)
{
ShiftCandles(candle);
return;
}
// Bullish patterns using stored values
var threeWhiteSoldiers = _prevOpen3 < _prevClose3 && _prevOpen2 < _prevClose2 && _prevOpen1 < _prevClose1 &&
_prevClose3 < _prevClose2 && _prevClose2 < _prevClose1;
var threeInsideUp = _prevOpen3 > _prevClose3 &&
Math.Abs(_prevClose2 - _prevOpen2) <= 0.6m * Math.Abs(_prevOpen3 - _prevClose3) &&
_prevClose2 > _prevOpen2 && _prevClose1 > _prevOpen1 && _prevClose1 > _prevOpen3;
// Bearish patterns
var threeBlackCrows = _prevOpen3 > _prevClose3 && _prevOpen2 > _prevClose2 && _prevOpen1 > _prevClose1 &&
_prevClose3 > _prevClose2 && _prevClose2 > _prevClose1;
var threeInsideDown = _prevOpen3 < _prevClose3 &&
Math.Abs(_prevClose2 - _prevOpen2) <= 0.6m * Math.Abs(_prevOpen3 - _prevClose3) &&
_prevClose2 < _prevOpen2 && _prevClose1 < _prevOpen1 && _prevClose1 < _prevOpen3;
var bullSignal = threeWhiteSoldiers || threeInsideUp;
var bearSignal = threeBlackCrows || threeInsideDown;
if (bullSignal && candle.ClosePrice > ma && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (bearSignal && candle.ClosePrice < ma && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
ShiftCandles(candle);
}
private void ShiftCandles(ICandleMessage candle)
{
_prevOpen3 = _prevOpen2; _prevClose3 = _prevClose2; _prevLow3 = _prevLow2;
_prevOpen2 = _prevOpen1; _prevClose2 = _prevClose1; _prevLow2 = _prevLow1;
_prevOpen1 = candle.OpenPrice; _prevClose1 = candle.ClosePrice; _prevLow1 = candle.LowPrice;
}
}
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.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bullish_reversal_strategy(Strategy):
def __init__(self):
super(bullish_reversal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._ma_period = self.Param("MaPeriod", 50) \
.SetDisplay("MA Period", "EMA length", "Parameters")
self._prev_open1 = 0.0
self._prev_close1 = 0.0
self._prev_low1 = 0.0
self._prev_open2 = 0.0
self._prev_close2 = 0.0
self._prev_low2 = 0.0
self._prev_open3 = 0.0
self._prev_close3 = 0.0
self._prev_low3 = 0.0
self._candle_count = 0
@property
def candle_type(self):
return self._candle_type.Value
@property
def ma_period(self):
return self._ma_period.Value
def OnReseted(self):
super(bullish_reversal_strategy, self).OnReseted()
self._prev_open1 = 0.0
self._prev_close1 = 0.0
self._prev_low1 = 0.0
self._prev_open2 = 0.0
self._prev_close2 = 0.0
self._prev_low2 = 0.0
self._prev_open3 = 0.0
self._prev_close3 = 0.0
self._prev_low3 = 0.0
self._candle_count = 0
def OnStarted2(self, time):
super(bullish_reversal_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.ma_period
self.SubscribeCandles(self.candle_type).Bind(ema, self.process_candle).Start()
def process_candle(self, candle, ma):
if candle.State != CandleStates.Finished:
return
self._candle_count += 1
if self._candle_count < 4:
self._shift_candles(candle)
return
mv = float(ma)
three_white_soldiers = (self._prev_open3 < self._prev_close3 and
self._prev_open2 < self._prev_close2 and
self._prev_open1 < self._prev_close1 and
self._prev_close3 < self._prev_close2 and
self._prev_close2 < self._prev_close1)
three_inside_up = (self._prev_open3 > self._prev_close3 and
abs(self._prev_close2 - self._prev_open2) <= 0.6 * abs(self._prev_open3 - self._prev_close3) and
self._prev_close2 > self._prev_open2 and
self._prev_close1 > self._prev_open1 and
self._prev_close1 > self._prev_open3)
three_black_crows = (self._prev_open3 > self._prev_close3 and
self._prev_open2 > self._prev_close2 and
self._prev_open1 > self._prev_close1 and
self._prev_close3 > self._prev_close2 and
self._prev_close2 > self._prev_close1)
three_inside_down = (self._prev_open3 < self._prev_close3 and
abs(self._prev_close2 - self._prev_open2) <= 0.6 * abs(self._prev_open3 - self._prev_close3) and
self._prev_close2 < self._prev_open2 and
self._prev_close1 < self._prev_open1 and
self._prev_close1 < self._prev_open3)
bull_signal = three_white_soldiers or three_inside_up
bear_signal = three_black_crows or three_inside_down
close = float(candle.ClosePrice)
if bull_signal and close > mv and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif bear_signal and close < mv and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._shift_candles(candle)
def _shift_candles(self, candle):
self._prev_open3 = self._prev_open2
self._prev_close3 = self._prev_close2
self._prev_low3 = self._prev_low2
self._prev_open2 = self._prev_open1
self._prev_close2 = self._prev_close1
self._prev_low2 = self._prev_low1
self._prev_open1 = float(candle.OpenPrice)
self._prev_close1 = float(candle.ClosePrice)
self._prev_low1 = float(candle.LowPrice)
def CreateClone(self):
return bullish_reversal_strategy()