The Four Screens strategy trades using Heikin-Ashi candles across four timeframes: 5, 15, 30, and 60 minutes.
It goes long when all timeframes show bullish candles and goes short when all show bearish candles.
Stop-loss and take-profit levels are set in points with optional trailing stop.
How it works
Subscribes to candle streams for 5, 15, 30 and 60 minutes.
Calculates Heikin-Ashi open and close for each candle.
Marks each timeframe as bullish or bearish.
Enters long when all are bullish, enters short when all are bearish.
Uses StartProtection to apply stop-loss, take-profit and optional trailing.
Parameters
CandleType – base timeframe for 5 minute candles.
StopLossPoints – stop-loss distance in points.
TakeProfitPoints – take-profit distance in points.
UseTrailing – enable trailing stop (true/false).
The trading volume is defined by the strategy Volume property.
Notes
Works with high-level API using SubscribeCandles and Bind.
Processes only finished candles.
Comments in code are provided in English.
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 using Heikin-Ashi color with EMA filter.
/// Buys when HA turns bullish and price above EMA.
/// Sells when HA turns bearish and price below EMA.
/// </summary>
public class FourScreensStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHaOpen;
private decimal _prevHaClose;
private bool _prevIsBull;
private bool _hasPrev;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FourScreensStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHaOpen = 0;
_prevHaClose = 0;
_prevIsBull = false;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
SubscribeCandles(CandleType)
.Bind(ema, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
// Calculate Heikin-Ashi
decimal haOpen;
decimal haClose;
if (_prevHaOpen == 0 && _prevHaClose == 0)
{
haOpen = (candle.OpenPrice + candle.ClosePrice) / 2m;
haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
}
else
{
haOpen = (_prevHaOpen + _prevHaClose) / 2m;
haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4m;
}
var isBull = haClose > haOpen;
var close = candle.ClosePrice;
if (_hasPrev)
{
// Buy: HA turns bullish + price above EMA
if (isBull && !_prevIsBull && close > emaValue && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
// Sell: HA turns bearish + price below EMA
else if (!isBull && _prevIsBull && close < emaValue && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
}
_prevHaOpen = haOpen;
_prevHaClose = haClose;
_prevIsBull = isBull;
_hasPrev = true;
}
}
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 four_screens_strategy(Strategy):
def __init__(self):
super(four_screens_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA trend filter period", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._prev_is_bull = False
self._has_prev = False
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(four_screens_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(ema, self.ProcessCandle) \
.Start()
def ProcessCandle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
if self._prev_ha_open == 0.0 and self._prev_ha_close == 0.0:
ha_open = (float(candle.OpenPrice) + float(candle.ClosePrice)) / 2.0
ha_close = (float(candle.OpenPrice) + float(candle.HighPrice)
+ float(candle.LowPrice) + float(candle.ClosePrice)) / 4.0
else:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
ha_close = (float(candle.OpenPrice) + float(candle.HighPrice)
+ float(candle.LowPrice) + float(candle.ClosePrice)) / 4.0
is_bull = ha_close > ha_open
close = float(candle.ClosePrice)
if self._has_prev:
if is_bull and not self._prev_is_bull and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif not is_bull and self._prev_is_bull and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
self._prev_is_bull = is_bull
self._has_prev = True
def OnReseted(self):
super(four_screens_strategy, self).OnReseted()
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._prev_is_bull = False
self._has_prev = False
def CreateClone(self):
return four_screens_strategy()