Стратегия Innocent Heikin Ashi Ethereum
Стратегия открывает длинную позицию по Ethereum, когда серия медвежьих свечей ниже EMA50 сменяется бычьей свечой выше EMA50. Стоп размещается на минимуме последних 28 баров, тейк рассчитывается по множителю RiskReward. Опция Moon Mode позволяет входить при цене выше EMA200. Позиция может закрываться досрочно по сигналам продажи или ловушек.
Детали
- Критерии входа:
- Long: минимум
ConfirmationLevelкрасных свечей ниже EMA50, затем зелёная свеча выше EMA50. - Агрессивный: если
EnableMoonModeвключён и цена выше EMA200.
- Long: минимум
- Long/Short: только Long.
- Критерии выхода:
- Стоп-лосс на минимуме последних 28 баров.
- Тейк-профит по множителю
RiskReward. - Дополнительные сигналы продажи или ловушки для раннего выхода.
- Стопы: да.
- Значения по умолчанию:
RiskReward= 1.ConfirmationLevel= 1.EnableMoonMode= true.
- Фильтры:
- Категория: Trend following
- Направление: Long
- Индикаторы: EMA
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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()