Innocent Heikin Ashi Ethereum Strategy
This strategy goes long on Ethereum when a sequence of bearish candles under the EMA50 is followed by a bullish candle above the EMA50. A stop loss is placed at the lowest low of the recent 28 bars and a take profit is calculated with the RiskReward multiplier. Optional Moon Mode permits entries above the EMA200. The position may close early on sell or trap signals.
Details
- Entry Criteria:
- Long: at least
ConfirmationLevelred candles below EMA50 followed by a green candle above EMA50. - Aggressive: if
EnableMoonModeis true and price is above EMA200.
- Long: at least
- Long/Short: Long only.
- Exit Criteria:
- Stop loss at the lowest low of the last 28 bars.
- Take profit using
RiskRewardmultiplier. - Optional sell or trap signals for early exit.
- Stops: Yes.
- Default Values:
RiskReward= 1.ConfirmationLevel= 1.EnableMoonMode= true.
- Filters:
- Category: Trend following
- Direction: Long
- Indicators: EMA
- 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>
/// Goes long on Ethereum after bearish activity below EMA50 followed by a
/// bullish candle above EMA50.
/// </summary>
public class InnocentHeikinAshiEthereumStrategy : Strategy {
private readonly StrategyParam<decimal> _riskReward;
private readonly StrategyParam<int> _confirmationLevel;
private readonly StrategyParam<bool> _enableMoonMode;
private readonly StrategyParam<bool> _showSellSignals;
private readonly StrategyParam<bool> _showBullTraps;
private readonly StrategyParam<bool> _showBearTraps;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema50;
private ExponentialMovingAverage _ema200;
private Lowest _lowest;
private int? _lastRedVectorBelowEma50;
private int? _lastBuySignalIndex;
private int? _lastSellSignalIndex;
private int _redCountUnderEma50;
private int _greenCountAboveEma200;
private int _barIndex;
private decimal _prevHaOpen;
private decimal _prevHaClose;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Initializes a new instance of the <see
/// cref="InnocentHeikinAshiEthereumStrategy"/> class.
/// </summary>
public InnocentHeikinAshiEthereumStrategy() {
_riskReward =
Param(nameof(RiskReward), 1m)
.SetGreaterThanZero()
.SetDisplay("Risk/Reward", "Take profit to stop ratio", "Risk")
.SetOptimize(0.5m, 3m, 0.5m);
_confirmationLevel =
Param(nameof(ConfirmationLevel), 1)
.SetNotNegative()
.SetDisplay(
"Confirmation Level",
"Number of red candles below EMA50 required before entry",
"General");
_enableMoonMode =
Param(nameof(EnableMoonMode), true)
.SetDisplay("Enable Moon Mode", "Allow entries above EMA200",
"General");
_showSellSignals = Param(nameof(ShowSellSignals), true)
.SetDisplay("Show Sell Signals",
"Close on sell signals", "General");
_showBullTraps =
Param(nameof(ShowBullTraps), true)
.SetDisplay("Show Bull Traps",
"Close if next candle after buy is red", "General");
_showBearTraps =
Param(nameof(ShowBearTraps), true)
.SetDisplay("Show Bear Traps", "Close if sell signal fails",
"General");
_candleType =
Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <summary>
/// Risk/reward multiplier.
/// </summary>
public decimal RiskReward {
get => _riskReward.Value;
set => _riskReward.Value = value;
}
/// <summary>
/// Required number of red candles below EMA50.
/// </summary>
public int ConfirmationLevel {
get => _confirmationLevel.Value;
set => _confirmationLevel.Value = value;
}
/// <summary>
/// Allow aggressive entries above EMA200.
/// </summary>
public bool EnableMoonMode {
get => _enableMoonMode.Value;
set => _enableMoonMode.Value = value;
}
/// <summary>
/// Close position on sell signals.
/// </summary>
public bool ShowSellSignals {
get => _showSellSignals.Value;
set => _showSellSignals.Value = value;
}
/// <summary>
/// Close if the candle after a buy is bearish.
/// </summary>
public bool ShowBullTraps {
get => _showBullTraps.Value;
set => _showBullTraps.Value = value;
}
/// <summary>
/// Close if sell signal fails.
/// </summary>
public bool ShowBearTraps {
get => _showBearTraps.Value;
set => _showBearTraps.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType {
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)>
GetWorkingSecurities() {
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted() {
base.OnReseted();
_lastRedVectorBelowEma50 = null;
_lastBuySignalIndex = null;
_lastSellSignalIndex = null;
_redCountUnderEma50 = 0;
_greenCountAboveEma200 = 0;
_barIndex = 0;
_prevHaOpen = 0m;
_prevHaClose = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time) {
base.OnStarted2(time);
StartProtection(null, null);
_ema50 = new ExponentialMovingAverage { Length = 50 };
_ema200 = new ExponentialMovingAverage { Length = 200 };
_lowest = new Lowest { Length = 28 };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_ema50, _ema200, _lowest, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema50,
decimal ema200, decimal lowest) {
if (candle.State != CandleStates.Finished)
return;
decimal haOpen;
decimal haClose;
if (_barIndex == 0) {
haOpen = (candle.OpenPrice + candle.ClosePrice) / 2;
haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice +
candle.ClosePrice) /
4;
} else {
haOpen = (_prevHaOpen + _prevHaClose) / 2;
haClose = (candle.OpenPrice + candle.HighPrice + candle.LowPrice +
candle.ClosePrice) /
4;
}
var isGreen = haClose > haOpen;
var isRed = !isGreen;
if (isRed && candle.ClosePrice < ema50)
_redCountUnderEma50++;
if (isRed && candle.OpenPrice < ema50 && candle.ClosePrice < ema50)
_lastRedVectorBelowEma50 = _barIndex;
if (isGreen && candle.OpenPrice > ema200 && candle.OpenPrice > ema50)
_lastSellSignalIndex = _barIndex;
if (isGreen && candle.ClosePrice > ema200)
_greenCountAboveEma200++;
if (_lastRedVectorBelowEma50.HasValue && isGreen) {
_stopPrice = lowest;
_takePrice = candle.ClosePrice +
(candle.ClosePrice - _stopPrice) * RiskReward;
}
var canBuy =
_lastRedVectorBelowEma50.HasValue && isGreen &&
candle.OpenPrice > ema50 &&
(_lastBuySignalIndex == null || _barIndex > _lastBuySignalIndex);
if (canBuy) {
if (candle.ClosePrice < ema200 &&
_redCountUnderEma50 >= ConfirmationLevel) {
BuyMarket();
_lastBuySignalIndex = _barIndex;
_lastRedVectorBelowEma50 = null;
_redCountUnderEma50 = 0;
} else if (EnableMoonMode && candle.ClosePrice > ema200 &&
_redCountUnderEma50 >= ConfirmationLevel) {
BuyMarket();
_lastBuySignalIndex = _barIndex;
_lastRedVectorBelowEma50 = null;
_redCountUnderEma50 = 0;
}
}
if (Position > 0) {
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
SellMarket();
}
if (ShowSellSignals && _lastSellSignalIndex.HasValue && isRed &&
candle.OpenPrice > ema200 && candle.ClosePrice > ema200 &&
_barIndex == _lastSellSignalIndex + 1) {
if (_greenCountAboveEma200 >= ConfirmationLevel) {
SellMarket();
_lastSellSignalIndex = null;
_greenCountAboveEma200 = 0;
}
}
if (ShowBullTraps && _lastBuySignalIndex.HasValue &&
_barIndex == _lastBuySignalIndex + 1 && isRed)
SellMarket();
if (ShowBearTraps && _lastSellSignalIndex.HasValue &&
_barIndex == _lastSellSignalIndex + 1 && isGreen) {
SellMarket();
_lastSellSignalIndex = null;
_greenCountAboveEma200 = 0;
}
_prevHaOpen = haOpen;
_prevHaClose = haClose;
_barIndex++;
}
}
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 DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Lowest
from StockSharp.Algo.Strategies import Strategy
class innocent_heikin_ashi_ethereum_strategy(Strategy):
def __init__(self):
super(innocent_heikin_ashi_ethereum_strategy, self).__init__()
self._risk_reward = self.Param("RiskReward", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Risk/Reward", "Take profit to stop ratio", "Risk")
self._confirmation_level = self.Param("ConfirmationLevel", 1) \
.SetDisplay("Confirmation Level", "Number of red candles below EMA50 required before entry", "General")
self._enable_moon_mode = self.Param("EnableMoonMode", True) \
.SetDisplay("Enable Moon Mode", "Allow entries above EMA200", "General")
self._show_sell_signals = self.Param("ShowSellSignals", True) \
.SetDisplay("Show Sell Signals", "Close on sell signals", "General")
self._show_bull_traps = self.Param("ShowBullTraps", True) \
.SetDisplay("Show Bull Traps", "Close if next candle after buy is red", "General")
self._show_bear_traps = self.Param("ShowBearTraps", True) \
.SetDisplay("Show Bear Traps", "Close if sell signal fails", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._last_red_vector_below_ema50 = None
self._last_buy_signal_index = None
self._last_sell_signal_index = None
self._red_count_under_ema50 = 0
self._green_count_above_ema200 = 0
self._bar_index = 0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._stop_price = 0.0
self._take_price = 0.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(innocent_heikin_ashi_ethereum_strategy, self).OnReseted()
self._last_red_vector_below_ema50 = None
self._last_buy_signal_index = None
self._last_sell_signal_index = None
self._red_count_under_ema50 = 0
self._green_count_above_ema200 = 0
self._bar_index = 0
self._prev_ha_open = 0.0
self._prev_ha_close = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def OnStarted2(self, time):
super(innocent_heikin_ashi_ethereum_strategy, self).OnStarted2(time)
ema50 = ExponentialMovingAverage()
ema50.Length = 50
ema200 = ExponentialMovingAverage()
ema200.Length = 200
lowest = Lowest()
lowest.Length = 28
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema50, ema200, lowest, self.OnProcess).Start()
def OnProcess(self, candle, ema50_val, ema200_val, lowest_val):
if candle.State != CandleStates.Finished:
return
open_p = float(candle.OpenPrice)
high_p = float(candle.HighPrice)
low_p = float(candle.LowPrice)
close_p = float(candle.ClosePrice)
ema50 = float(ema50_val)
ema200 = float(ema200_val)
lowest_v = float(lowest_val)
rr = float(self._risk_reward.Value)
conf = self._confirmation_level.Value
if self._bar_index == 0:
ha_open = (open_p + close_p) / 2.0
ha_close = (open_p + high_p + low_p + close_p) / 4.0
else:
ha_open = (self._prev_ha_open + self._prev_ha_close) / 2.0
ha_close = (open_p + high_p + low_p + close_p) / 4.0
is_green = ha_close > ha_open
is_red = not is_green
if is_red and close_p < ema50:
self._red_count_under_ema50 += 1
if is_red and open_p < ema50 and close_p < ema50:
self._last_red_vector_below_ema50 = self._bar_index
if is_green and open_p > ema200 and open_p > ema50:
self._last_sell_signal_index = self._bar_index
if is_green and close_p > ema200:
self._green_count_above_ema200 += 1
if self._last_red_vector_below_ema50 is not None and is_green:
self._stop_price = lowest_v
self._take_price = close_p + (close_p - self._stop_price) * rr
can_buy = (self._last_red_vector_below_ema50 is not None and
is_green and open_p > ema50 and
(self._last_buy_signal_index is None or self._bar_index > self._last_buy_signal_index))
if can_buy:
if close_p < ema200 and self._red_count_under_ema50 >= conf:
self.BuyMarket()
self._last_buy_signal_index = self._bar_index
self._last_red_vector_below_ema50 = None
self._red_count_under_ema50 = 0
elif self._enable_moon_mode.Value and close_p > ema200 and self._red_count_under_ema50 >= conf:
self.BuyMarket()
self._last_buy_signal_index = self._bar_index
self._last_red_vector_below_ema50 = None
self._red_count_under_ema50 = 0
if self.Position > 0:
if low_p <= self._stop_price or high_p >= self._take_price:
self.SellMarket()
if self._show_sell_signals.Value and self._last_sell_signal_index is not None and is_red:
if open_p > ema200 and close_p > ema200 and self._bar_index == self._last_sell_signal_index + 1:
if self._green_count_above_ema200 >= conf:
self.SellMarket()
self._last_sell_signal_index = None
self._green_count_above_ema200 = 0
if self._show_bull_traps.Value and self._last_buy_signal_index is not None:
if self._bar_index == self._last_buy_signal_index + 1 and is_red:
self.SellMarket()
if self._show_bear_traps.Value and self._last_sell_signal_index is not None:
if self._bar_index == self._last_sell_signal_index + 1 and is_green:
self.SellMarket()
self._last_sell_signal_index = None
self._green_count_above_ema200 = 0
self._prev_ha_open = ha_open
self._prev_ha_close = ha_close
self._bar_index += 1
def CreateClone(self):
return innocent_heikin_ashi_ethereum_strategy()