ETH Signal 15m Strategy
The ETH Signal 15m strategy uses the Supertrend indicator to detect direction changes and the RSI to filter entries. A long position opens when the Supertrend direction decreases and RSI is below the overbought level. A short position opens when the Supertrend direction increases and RSI is above the oversold level. Exits use ATR-based stop loss and take profit.
Details
- Entry Criteria:
- Long: Supertrend direction decreases and RSI below
RsiOverbought. - Short: Supertrend direction increases and RSI above
RsiOversold.
- Long: Supertrend direction decreases and RSI below
- Long/Short: Both sides.
- Exit Criteria: ATR-based stop loss and take profit.
- Stops: 4×ATR stop loss, 2×ATR long take profit, 2.237×ATR short take profit.
- Default Values:
AtrPeriod= 12Factor= 2.76RsiLength= 12RsiOverbought= 70RsiOversold= 30
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: Supertrend, RSI, ATR
- Stops: ATR stop loss and take profit
- Complexity: Low
- Timeframe: 15m
- 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>
/// ETH signal 15m strategy.
/// A SuperTrend flip to an uptrend goes long when RSI is below RsiOverbought, a flip to a downtrend goes short when RSI is above
/// RsiOversold, reversing an opposite position. The stop lies 4 ATR from the entry, the take profit 2 ATR for longs and 2.237 ATR
/// for shorts.
/// </summary>
public class EthSignal15mStrategy : Strategy
{
private const decimal _stopAtr = 4m;
private const decimal _longTakeAtr = 2m;
private const decimal _shortTakeAtr = 2.237m;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _factor;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<DataType> _candleType;
private bool? _prevUpTrend;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// ATR period of SuperTrend and the exits.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// SuperTrend factor.
/// </summary>
public decimal Factor
{
get => _factor.Value;
set => _factor.Value = value;
}
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level below which longs are allowed.
/// </summary>
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// RSI level above which shorts are allowed.
/// </summary>
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EthSignal15mStrategy()
{
_atrPeriod = Param(nameof(AtrPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period of SuperTrend and the exits", "SuperTrend");
_factor = Param(nameof(Factor), 2.76m)
.SetGreaterThanZero()
.SetDisplay("Factor", "SuperTrend factor", "SuperTrend");
_rsiLength = Param(nameof(RsiLength), 12)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "RSI");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI level below which longs are allowed", "RSI");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI level above which shorts are allowed", "RSI");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUpTrend = null;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevUpTrend = null;
var superTrend = new SuperTrend { Length = AtrPeriod, Multiplier = Factor };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(superTrend, rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, superTrend);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue superTrendValue, IIndicatorValue rsiValue, IIndicatorValue atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!superTrendValue.IsFormed || !rsiValue.IsFormed || !atrValue.IsFormed || superTrendValue is not SuperTrendIndicatorValue trend)
return;
var prevUpTrend = _prevUpTrend;
_prevUpTrend = trend.IsUpTrend;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0 && (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice))
{
SellMarket(Position);
return;
}
if (Position < 0 && (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice))
{
BuyMarket(-Position);
return;
}
if (prevUpTrend is not bool wasUp)
return;
var rsi = rsiValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var close = candle.ClosePrice;
if (!wasUp && trend.IsUpTrend && rsi < RsiOverbought && Position <= 0)
{
_stopPrice = close - atr * _stopAtr;
_takePrice = close + atr * _longTakeAtr;
BuyMarket(Volume + Math.Abs(Position));
}
else if (wasUp && !trend.IsUpTrend && rsi > RsiOversold && Position >= 0)
{
_stopPrice = close + atr * _stopAtr;
_takePrice = close - atr * _shortTakeAtr;
SellMarket(Volume + Math.Abs(Position));
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SuperTrend, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
STOP_ATR = 4.0
LONG_TAKE_ATR = 2.0
SHORT_TAKE_ATR = 2.237
class eth_signal_15m_strategy(Strategy):
"""
ETH signal 15m strategy.
A SuperTrend flip to an uptrend goes long when RSI is below RsiOverbought, a flip to a downtrend goes short when RSI is above
RsiOversold, reversing an opposite position. The stop lies 4 ATR from the entry, the take profit 2 ATR for longs and 2.237 ATR
for shorts.
"""
def __init__(self):
super(eth_signal_15m_strategy, self).__init__()
self._atr_period = self.Param("AtrPeriod", 12).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period of SuperTrend and the exits", "SuperTrend")
self._factor = self.Param("Factor", 2.76).SetGreaterThanZero().SetDisplay("Factor", "SuperTrend factor", "SuperTrend")
self._rsi_length = self.Param("RsiLength", 12).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "RSI")
self._rsi_overbought = self.Param("RsiOverbought", 70.0).SetDisplay("RSI Overbought", "RSI level below which longs are allowed", "RSI")
self._rsi_oversold = self.Param("RsiOversold", 30.0).SetDisplay("RSI Oversold", "RSI level above which shorts are allowed", "RSI")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_up_trend = None
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(eth_signal_15m_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(eth_signal_15m_strategy, self).OnStarted2(time)
self._prev_up_trend = None
super_trend = SuperTrend()
super_trend.Length = self._atr_period.Value
super_trend.Multiplier = Decimal(float(self._factor.Value))
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(super_trend, rsi, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, super_trend)
self.DrawOwnTrades(area)
def _process_candle(self, candle, super_trend_value, rsi_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not super_trend_value.IsFormed or not rsi_value.IsFormed or not atr_value.IsFormed:
return
is_up = bool(super_trend_value.IsUpTrend)
was_up = self._prev_up_trend
self._prev_up_trend = is_up
if not self.IsFormedAndOnlineAndAllowTrading():
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0 and (low <= self._stop_price or high >= self._take_price):
self.SellMarket(self.Position)
return
if self.Position < 0 and (high >= self._stop_price or low <= self._take_price):
self.BuyMarket(-self.Position)
return
if was_up is None:
return
rsi = float(rsi_value.GetValue[Decimal](None))
atr = float(atr_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
if not was_up and is_up and rsi < float(self._rsi_overbought.Value) and self.Position <= 0:
self._stop_price = close - atr * STOP_ATR
self._take_price = close + atr * LONG_TAKE_ATR
self.BuyMarket(self.Volume + abs(self.Position))
elif was_up and not is_up and rsi > float(self._rsi_oversold.Value) and self.Position >= 0:
self._stop_price = close + atr * STOP_ATR
self._take_price = close - atr * SHORT_TAKE_ATR
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return eth_signal_15m_strategy()