Morning Star Pattern Strategy
The Morning Star is a bullish candlestick formation that signals a potential bottom after a decline. It consists of a large bearish candle, a small indecisive candle, and a strong bullish candle that closes above the midpoint of the first bar.
Testing indicates an average annual return of about 97%. It performs best in the crypto market.
This strategy tracks sequences of three candles. When the pattern appears a long position is opened with a stop placed below the small middle candle. Exits occur once price rises above the high of the confirmation bar or if the stop is reached.
Because the pattern often sparks quick recoveries from oversold conditions, trades are usually short lived, capturing the initial thrust higher.
Details
- Entry Criteria: Three-candle Morning Star pattern.
- Long/Short: Long only.
- Exit Criteria: Price above confirmation bar high or stop-loss.
- Stops: Yes, below middle candle low.
- Default Values:
CandleType= 5 minuteStopLossPercent= 1
- Filters:
- Category: Pattern
- Direction: Long
- Indicators: Candlestick
- Stops: Yes
- Complexity: Intermediate
- 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>
/// Morning Star candle pattern strategy.
/// Morning Star: 1st bearish, 2nd small body (doji), 3rd bullish closing above midpoint of 1st.
/// Evening Star (reverse): 1st bullish, 2nd small body, 3rd bearish closing below midpoint of 1st.
/// Uses SMA for exit signals.
/// </summary>
public class MorningStarStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cooldownBars;
private ICandleMessage _bar1;
private ICandleMessage _bar2;
private int _cooldown;
/// <summary>
/// MA Period.
/// </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>
/// Cooldown bars.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MorningStarStrategy()
{
_maPeriod = Param(nameof(MAPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period for SMA", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_cooldownBars = Param(nameof(CooldownBars), 500)
.SetRange(1, 1000)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_bar1 = null;
_bar2 = null;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bar1 = null;
_bar2 = null;
_cooldown = 0;
var sma = new SimpleMovingAverage { Length = MAPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_cooldown > 0)
{
_cooldown--;
_bar1 = _bar2;
_bar2 = candle;
return;
}
if (_bar1 != null && _bar2 != null)
{
// Morning Star (bullish reversal)
var firstBearish = _bar1.ClosePrice < _bar1.OpenPrice;
var firstBody = Math.Abs(_bar1.OpenPrice - _bar1.ClosePrice);
var secondBody = Math.Abs(_bar2.OpenPrice - _bar2.ClosePrice);
var secondSmall = firstBody > 0 && secondBody < firstBody * 0.5m;
var thirdBullish = candle.ClosePrice > candle.OpenPrice;
var firstMid = (_bar1.HighPrice + _bar1.LowPrice) / 2;
var morningStar = firstBearish && secondSmall && thirdBullish && candle.ClosePrice > firstMid;
// Evening Star (bearish reversal)
var firstBullish = _bar1.ClosePrice > _bar1.OpenPrice;
var thirdBearish = candle.ClosePrice < candle.OpenPrice;
var eveningStar = firstBullish && secondSmall && thirdBearish && candle.ClosePrice < firstMid;
if (Position == 0 && morningStar)
{
BuyMarket();
_cooldown = CooldownBars;
}
else if (Position == 0 && eveningStar)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position > 0 && candle.ClosePrice < smaValue)
{
SellMarket();
_cooldown = CooldownBars;
}
else if (Position < 0 && candle.ClosePrice > smaValue)
{
BuyMarket();
_cooldown = CooldownBars;
}
}
_bar1 = _bar2;
_bar2 = candle;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class morning_star_strategy(Strategy):
"""
Morning Star candle pattern strategy.
Morning Star: 1st bearish, 2nd small body (doji), 3rd bullish closing above midpoint of 1st.
Evening Star (reverse): 1st bullish, 2nd small body, 3rd bearish closing below midpoint of 1st.
Uses SMA for exit signals.
"""
def __init__(self):
super(morning_star_strategy, self).__init__()
self._ma_period = self.Param("MAPeriod", 20).SetDisplay("MA Period", "Period for SMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._cooldown_bars = self.Param("CooldownBars", 500).SetDisplay("Cooldown Bars", "Bars to wait between trades", "General")
self._bar1 = None
self._bar2 = None
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(morning_star_strategy, self).OnReseted()
self._bar1 = None
self._bar2 = None
self._cooldown = 0
def OnStarted2(self, time):
super(morning_star_strategy, self).OnStarted2(time)
self._bar1 = None
self._bar2 = None
self._cooldown = 0
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, sma_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
self._bar1 = self._bar2
self._bar2 = candle
return
if self._bar1 is not None and self._bar2 is not None:
# Morning Star (bullish reversal)
first_bearish = self._bar1.ClosePrice < self._bar1.OpenPrice
first_body = abs(float(self._bar1.OpenPrice) - float(self._bar1.ClosePrice))
second_body = abs(float(self._bar2.OpenPrice) - float(self._bar2.ClosePrice))
second_small = first_body > 0 and second_body < first_body * 0.5
third_bullish = candle.ClosePrice > candle.OpenPrice
first_mid = (float(self._bar1.HighPrice) + float(self._bar1.LowPrice)) / 2.0
morning_star = first_bearish and second_small and third_bullish and float(candle.ClosePrice) > first_mid
# Evening Star (bearish reversal)
first_bullish = self._bar1.ClosePrice > self._bar1.OpenPrice
third_bearish = candle.ClosePrice < candle.OpenPrice
evening_star = first_bullish and second_small and third_bearish and float(candle.ClosePrice) < first_mid
sv = float(sma_val)
close = float(candle.ClosePrice)
cd = self._cooldown_bars.Value
if self.Position == 0 and morning_star:
self.BuyMarket()
self._cooldown = cd
elif self.Position == 0 and evening_star:
self.SellMarket()
self._cooldown = cd
elif self.Position > 0 and close < sv:
self.SellMarket()
self._cooldown = cd
elif self.Position < 0 and close > sv:
self.BuyMarket()
self._cooldown = cd
self._bar1 = self._bar2
self._bar2 = candle
def CreateClone(self):
return morning_star_strategy()